Vol. I  ·  No. 166 Established 2026  ·  AI-Generated Daily Free to Read  ·  Free to Print

The Trilogy Times

All the news that's fit to generate  —  AI • Business • Innovation
MONDAY, JUNE 15, 2026 Powered by Anthropic Claude  ·  Published on Klair Trilogy International © 2026
🖶 Download PDF 🖿 Print 📰 All Editions
Today's Edition

Merger Mania: $32 Billion in Deals Hit the Tape — While Synopsys Cuts 2,800

Fox snags Roku, Coursera bags Udemy, fitness giants wed — and Silicon Valley's chip-design king swings the axe.

NEW YORK — Wall Street's tape ran hot this morning. Fox Corp. is buying Roku outright for $22 billion, Coursera is swallowing rival Udemy in a $2.5 billion online-learning combine, and Playlist and EGYM sealed a $7.5 billion fitness-tech tie-up. Tesla's board is twisting arms to bless Elon Musk's corporate reshuffle.

The dealmakers ran a marathon. The line workers ran for cover.

Fox plays the biggest hand at the table. Twenty-two billion dollars buys Roku's smart-TV network and every inch of home-screen real estate that goes with it. Fox's TV slate and the Tubi streamer fold into Roku's pipes — Fox content jumps the queue on every Roku-powered set, and the cord-cutters never see it coming.

Coursera's hand is smaller but the play is louder. The deal mashes the two biggest names in MOOCs — those open online courses that promised back in 2012 to reinvent the schoolhouse — into a single $2.5 billion combine. The catch: MOOC outfits have spent a decade hunting profits and largely come up empty.

EGYM and Playlist stitched together what they're calling the world's largest gym-tech outfit. Hardware, software, connected treadmills, the whole locker room — all under one roof at a $7.5 billion sticker price.

Musk's bid is a different animal entirely. The Tesla chief wants shareholders to bless a pay-and-restructuring package that pulls the carmaker tighter to xAI and his X social outfit. The Times asked the questions every shareholder should: how much, for what, and who foots the bill?

Now the kicker. Synopsys, the Sunnyvale chip-design giant, is firing up to 2,800 workers — even as it digests its Ansys acquisition. The EDA outfit says costs need trimming.

The math is plain. Deals get bigger, payrolls get smaller. Wall Street rings the bell, and Main Street ducks.

This reporter watched the roll-up game a long while. Down in Austin, Joe Liemandt's Trilogy outfit plays a quieter version — ESW Capital scoops up enterprise software shops at one to two times revenue, runs them lean through the Crossover talent platform, and holds them forever. No $22 billion fireworks — just 75-plus brand names quietly stacked on the shelf.

Today the loudmouths grabbed the headlines. Tomorrow's tape tells who got the bargain.

Coursera to acquire Udemy to create $2.5B MOOC giant - Highe  ·  Playlist, EGYM Close $7.5B Merger, Forming Fitness Tech Gian  ·  The Numbers, and Questions, Behind Musk’s Mega-Merger - The

Crypto Leaves the Tunnel and Takes the Field With Wall Street

We are HERE, under the bright lights of the financial stadium, and crypto is no longer jogging laps in the parking lot. Market.us projects the Web3 blockchain market to grow at a 48.2% compound annual growth rate — a blistering pace signaling serious momentum.

The real action, however, is integration. Silicon Valley Bank framed this as crypto's year of connection with traditional finance. Custody, payments, tokenized assets, stablecoins and treasury systems are the new positions on the depth chart. After boom-bust cycles and exchange collapses, surviving Web3 players are selling infrastructure, not rebellion — less "burn down the banks," more "plug into the banks."

Marketing is shifting too. Web3 startups are no longer just recruiting enthusiasts but pitching CFOs, compliance teams and mainstream users who demand proof over slogans. Community-led growth and compliance-aware messaging are the new playbook.

The landscape is competitive: AI currently dominates capital flows, pressuring crypto to demonstrate practical value. The scoreboard reads clear — AI has momentum, Wall Street has distribution, and Web3 has a fresh opening if it can execute. Integration is the drive. Now crypto must move the chains.

Haiku of the Day  ·  Claude HaikuGrowth devours its own
While machines learn to think fast
We forget to pause
The New Yorker Style  ·  Art Desk
The New Yorker Style  ·  Art Desk
The Far Side Style  ·  Art Desk
The Far Side Style  ·  Art Desk
News in Brief
AI Startup Funding Is a Tale of Two Worlds
TEL AVIV — Monday.com's decision to launch a $200 million venture arm targeting Israeli workplace AI startups lands at a revealing moment: the global AI funding surge that dominates headlines is, by the data, concentrated in a handful of markets — and getting more so. Monday Ventures, the new entity, will back early- to growth-stage companies building AI tools for the enterprise, according to reports.
ANTITRUST HONEYMOON ENDS: DOJ AND FTC SIGNAL SUSTAINED BIG TECH SCRUTINY INTO 2026
WASHINGTON, D.C.
The Tower of Babel Problem: AI Alignment Enters Its Epistemological Crisis
CAMBRIDGE, MASSACHUSETTS — It could be argued—and preliminary evidence suggests, with considerable force—that the field of artificial intelligence alignment has arrived at what one might, borrowing liberally from Kuhnian philosophy of science, term a paradigmatic rupture: a moment in which the foundational assumptions of a discipline are revealed to be, if not entirely bankrupt, then at minimum in urgent need of renegotiation. The thesis, as articulated across a remarkable confluence of recent scholarly productions, is straightforwardly familiar: that autonomous systems must be aligned with human values, lest they pursue objectives deleterious to human flourishing (a position elaborated with characteristic institutional thoroughness by IBM's foundational exposition of alignment as a coherent sub-discipline).
The Surveillance State Is Adorable Now, and That's the Most Terrifying Sentence I've Ever Written
AUSTIN, TEXAS — There is a whale graveyard at the bottom of the Indian Ocean.
IN DEFENSE OF ABSURDITY: OWL PEOPLE, TIP REVOLTS, AND THE AI THAT MIGHT DESTROY US ALL
AUSTIN, TEXAS — There are mornings when I wake up, mainline my third espresso before my feet hit the floor, and stare at the ceiling wondering if the human race has collectively decided to just...
A Trilogy Company
Crossover
The world's top 1% remote talent, rigorously tested and ready to ship.
A Trilogy Company
Alpha School
AI-powered learning. Two hours a day. Academic results that defy belief.
A Trilogy Company
Skyvera
Next-generation telecom software — built for the networks of tomorrow.
A Trilogy Company
Klair
Your AI-first operating system. Every workflow. Every team. One platform.
A Trilogy Company
Trilogy
We buy good software businesses and turn them into great ones — with AI.
The Builder Desk  —  AI Builder Team
📅 Week in ReviewProduction Release

Builder Team Ships Across Five Systems, Rewires the Intelligence Layer

From a live SpaceX valuation engine to a hardened public admissions API to a triage bot that finally, actually works — the AI Builder Team spent seven days turning half-finished foundations into production-grade machinery.

Some weeks you can feel the gears lock in. This was one of them. The AI Builder Team shipped meaningful work across five repositories — Klair, Aerie, Surtr, Sindri, and trilogy-drones — touching everything from financial memo architecture to admissions geography to a workflow builder that didn't exist last Monday. The through-line was the same in every system: harden what exists, then build the next thing on top of something that won't collapse.

The biggest single arc of the week belonged to the SpaceX valuation surface in Klair, and the engine driving it was @sanketghia. He entered the week with a live Yahoo-backed ticker quote endpoint (#2991), added a hardened stock-data backend (#3007), wired a real-time Current Market pill to the UI (#2998), and then — in back-to-back PRs — redesigned the entire /spacex-valuation page into a single-table linear IPO model (#3010) before delivering a lockup-release waterfall that follows the What-If price in real time (#3014). That is a complete product arc in seven days. @ashwanth1109 laid the groundwork with the original waterfall matching the IPO recon sheet (#3009), and the two of them handed SVP leadership something they can actually use in a budget meeting.

The MFR memo campaign, led by @eric-tril, was quieter but arguably more consequential over the long arc. Eric spent the week doing the architectural work that almost nobody talks about and everybody eventually depends on. He decomposed the Group memo AI generation into a per-narrative NarrativeUnit registry (#3017) — 26 self-contained, auditable narrative units replacing scattered prompt fragments — then mirrored the pattern for the EBITDA memo (#3024) and extracted shared provenance types into a neutral module (#3022) to sever cross-package dependencies before the software memo decomposition lands. He also shipped YTD Financial Highlights with per-bullet stale-check and regeneration for both Group (#2993) and Software (#3006) memos, and matched the Software memo's UI formatting to the published reference memo (#3018). When the refactor work is done, every AI-generated sentence in the MFR will be independently regenerable, auditable, and testable. That is a different class of product.

In Surtr, @kevalshahtrilogy fixed something embarrassing and fixed it completely. The open-work guard that was supposed to prevent duplicate automated PRs had been silently failing open since June 8th — every single run — leaving roughly 86 open Automated PR items and grainne-pull opening one duplicate per hour (#360). He closed that hole, then discovered the triage reconciler responsible for promoting DynamoDB rows from pending to open had been disabled in production the entire time (#457). Both are now fixed and running. He also got @mercy — the team's PR review agent — upgraded to Opus 4.8 (#238) and gated on CI status so it never approves a red build (#239). Speaking of which: mercy got its own repo this week. The team's AI reviewer is growing up.

Over in Aerie, @benji-bizzell had a week that defies easy summary. He shipped the public Admissions API into full dashboard read coverage across Phases 0 through 4 (#374), hardened it against Convex read-limit failures with a new lint guardrail wired into CI (#385, #387), added custom role creation for admins (#389), hydrated student coordinates with parent-child fallback (#394), and fixed a production incident around unbounded reads that had surfaced three separate times in a week. He also fixed the capacity resolver to source from Rhodes currentCapacity consistently across Enrollments and Forecast (#388), and kept the whole thing deployable by preserving a legacy index during rollout (#381). @ashwanth1109 paralleled him with a full sprint on Consolidated dashboards — HeadCount, Programs, and Facilities tables on Financials Schools Actual vs Model (#353, #358, #369) plus TeamRoom contractor roster panels on Edu Performance (#347, #349).

And then there is trilogy-drones. Marcus — @marcusdAIy — put up a volume of PRs this week that I will charitably describe as ambitious. Among them: a bounded reviewer-to-addresser loop (#40), an LLM-as-judge scoring lane (#35), a prompt-profile comparison analytics suite (#31), and a Claude Code receipts import sidecar (#39). When I asked him about the week he said, predictably: "The eval infrastructure I shipped isn't 'underwhelming,' Mac — it's the observability layer that makes every other AI system on this team measurable. You're welcome to keep ignoring it. The data won't." Sure, Marcus. The data.

In Sindri, @mwrshah shipped the React Flow workflow builder — canvas state modeled as a discriminated union, inspection drawer editing, backend main-loop wiring for graph runs (#115) — plus skill folder upload with org-scoped import handling (#116) and WorkOS feature flag gating (#117). @benji-bizzell added the orchestration module foundation (#118). A workflow product is now visibly taking shape.

This team enters next week with cleaner memo architecture ready for the software decomposition, a live SpaceX valuation surface that moves with the market, an admissions API that is public and hardened, and a drone evaluation framework that is — whatever Marcus says — at least shipping. The foundation is set. Time to build on it.

Mac's Picks — Key PRs This Week  (click to expand)
#115 — 001-react-flow-nodes @mwrshah  no labels

## Summary

React Flow workflow builder + node authoring surface, the inspection drawer editing UX, and backend main-loop wiring for graph runs.

# Outputs

https://github.com/user-attachments/assets/840f7eef-ec93-4924-8796-c4460b87843c

<img width="1286" height="859" alt="image" src="https://github.com/user-attachments/assets/02458c44-1870-407d-a28a-51a7161fa22f" />

## Authoring + editing surface for workflow nodes

- New React Flow workflow builder (workflow-builder.tsx): canvas state modeled as a discriminated union of node/edge types so node.type / edge.type narrow cleanly.

- Open palette drag-and-drop — only Agent / Webhook / Script are droppable; Start and End are structural singletons (one each, seeded at creation, non-deletable). Dropping a node onto an existing edge splices it (source→newNode→target).

- Connection validation (isValidNewConnection): Start is source-only, End is target-only, no self-edges, duplicates, or cycles. Leaving a read unsatisfied is still allowed — it just renders broken.

- Bind picker (node-bind-picker.tsx) attaches/re-binds agent or script definitions; updateGraph mutation persists the canvas with debounced autosave (800ms) and a save-status indicator (saving / saved / error / invalid).

- Live graph diagnostics: unmet required reads mark nodes broken and paint their gated edges coral; thin dashed teal variable-flow edges illustrate which producer writes each global-state variable chip (non-interactive).

- Input schema authoring: "Choose…" adds an upstream key on pick; Add + adds a free, key-editable field for hardcoded values. All field keys editable.

## Drawer UI

- Config editor cutover: drop @uiw/react-json-view (alpha, key-only) for react-json-view-lite — read-only tree plus an Edit JSON toggle to a raw textarea, auto-saving on blur with heal-on-save validation.

- Binding control relocated into the Configuration tab, left of Edit JSON; node type no longer renders config under the header. Raw definition-id rows dropped.

- Webhook URL is now an inline editable detail. Focus fix: field cards keyed by index so editing a key no longer remounts the input.

- Unified Modal primitive (Cmd+Enter primary-action contract); all dialogs migrated.

## Backend wiring of the main loop

- Output pool flattened from per-node namespaces to a single global pool (mergeOutputsIntoGlobalPool); node and End-node inputs resolved by schema pick (pickSchemaValues, keyed on field.key) against the pool instead of ancestor-graph walking.

- Graph diagnostics extracted to workflow-state.ts (computeGraphDiagnostics) and shared by runtime + builder UI; assertWorkflowStartable gates runs on zero broken nodes.

- Schema validators deduped (shared schemaField), plus runner/runtime updates with expanded test coverage across workflowDefinitions, workflowNodeRunners, and workflowRuntime.

#360 — AERIE-365: Make Rhodes mutation attribution server-owned @YibinLongTrilogy  no labels

## Summary

Makes mutation attribution server-owned across the Aerie → Rhodes write path. Previously, the chatbot and remote MCP callers could supply authorUserId, actorUserId, or uploadedByUserId and Rhodes mutations would trust them — so a note, document upload, or audit log entry could be attributed to any user the model (or a malicious caller) named. Now those fields are removed from all external tool schemas, stripped defensively from incoming args, and always overwritten from the authenticated Aerie user before the mutation runs. The approver of a pending mutation never becomes the author — attribution always follows the authenticated proposer.

### Changes

Convex runtime — enforce attribution from auth

- chat/convex/rhodes/runtime/ids.tsinjectActorUserId now always overwrites identity fields with the authenticated actor's ID instead of only filling gaps, so caller-supplied values can never survive.

- chat/convex/rhodes/runtime/pendingLifecycle.tscreatePendingMutationHandler no longer accepts actorUserId as an arg; it stores actor._id from requireCanMutateTool and stamps the audit entry with the same authenticated actor.

- chat/convex/rhodes/mcp.ts — drops actorUserId from the createPendingMutation validator to match.

- chat/convex/rhodes/runtime/constants.tsTOOL_IDENTITY_FIELDS updated: addNote keys off actorUserId only; addChangeLogEntry now overwrites both authorUserId and actorUserId.

- chat/convex/rhodes/runtime/writes/noteWrites.tsaddNote resolves the author from actorUserId (server-injected) rather than the formerly caller-supplied authorUserId.

Aerie chatbot tools

- chat/lib/rhodes-mutation-tools.ts — removes authorUserId/uploadedByUserId/actorUserId from the Zod schemas for addNote, addChangeLogEntry, registerDocument, updateDocument, removeDocument, and the cost breakdown tools; prepareMutationInput also deletes all ATTRIBUTION_FIELDS defensively before proposing, in case args arrive through a path that bypasses schema validation.

Remote MCP server

- chat/rhodes-worker/mcp-server/tools/notes.ts, documents.ts, costBreakdown.ts, changeLog.ts — the same attribution fields are removed from the MCP tool input schemas and no longer forwarded in mutation args.

Tests

- chat/convex/rhodesMcpMutationParity.test.ts — regression tests for the exact failure mode: a proposer supplies a bogus author/uploader/actor ID (e.g. a spoofed identity), a different admin approves, and the note author, document uploader, and audit actor all resolve to the authenticated proposer.

- chat/lib/__tests__/agent.test.ts — asserts the chatbot tool schemas reject attribution fields and that handlers strip them before proposeRhodesMutation is called.

### Design Decisions

- Enforcement is layered rather than relying on schema removal alone: schemas reject the fields, prepareMutationInput deletes them, and injectActorUserId overwrites them server-side. Even if a future tool schema regresses, the Convex runtime still wins.

- Attribution binds to the proposer at createPendingMutation time, not the approver at approval time — the person who initiated the change is the author, and the approval is recorded separately.

## Test Plan

- [x] pnpm typecheck and pnpm biome check pass (lefthook pre-commit on all four commits)

- [x] New parity tests cover spoofed authorUserId/uploadedByUserId/actorUserId with third-party approval

- [x] New agent tests verify chatbot schemas reject attribution fields and handlers strip them

- [ ] Reviewer: confirm no other Rhodes MCP tool still exposes an attribution field

#374 — Public API: ontology directory + full dashboard read coverage (Phases 0-4) @benji-bizzell  no labels

## Summary

Extends the public Admissions API (#364) into full read coverage of every live dashboard surface, plus the ontology directory foundation. Six commits, one phase each — see API.md for the locked decisions and roadmap this implements.

Phase 0 — scope eligibility gating. external.api on a capability now flips true only when a public route actually serves it; an exact-set contracts test enforces the invariant. The key scope picker never offers dead scopes.

Phase 1 — ontology directory (School naming locked). New chat/convex/ontology/ registry: opaque ULID-shaped public IDs (sch_/prog_/site_/mkt_/met_) minted once per source anchor (dim_school / HubSpot / Rhodes), alias + provenance-bearing relationship edges, idempotent rebuild on a 6-hour cron. Routes: GET /v1/schools, GET /v1/schools/{id} (include=aliases,relationships), GET /v1/relationships. Public-API School = the SIS-anchored membership grain, deliberately distinct from legacy program-grain dashboard schools[].

Phase 2 — admissions completion. Demographics, events, camps, community (aggregate + paginated drilldowns). Camp registration rows intentionally exclude the child medical block (DOB, allergies, medical conditions); tests assert those fields never serialize by key name, by seeded value, and by an exact row key-set pin.

Phase 3 — operations reads. Portfolio, buildout, diligence, operating, real-estate — dashboard-tab-aligned rows keyed by Rhodes site slug (joins to ontology via aliases). Curated server-side: no Convex IDs, no legal-entity block (EIN/counsel/lease terms), no site security, no Drive/Wrike folder IDs; DRI refs are name+email only. Diligence dueDiligence is a fixed projection (status, recommendation, scenario capacities/dates, per-area scores — no report links, capEx, or assessment comments), and real-estate rows are an explicit allowlist of the documented RealEstateSite fields.

Phase 4 — financials reads. School P&L (revenue/costs/consolidated), expense analysis (vendors/transactions, 120-day window enforced as structured 400), campus-QB mappings. snake_case dashboard internals convert to camelCase at the boundary (quarter keys like 2026-Q1 pass through). financials.educationPerformance.read stays API-ineligible — its Joe Charts dashboard is disabled in-app.

All dashboard handlers refactored into shared resolve* helpers with *ForApi internal queries, so app and API read identical rows; existing dashboard test suites confirm no in-app behavior change.

## Surface

28 public endpoints: admissions (14), operations (5), financials (6), ontology (3) — 21 new in this PR plus the 7 admissions endpoints already on main. 16 route scopes are API-eligible (the api.use meta-gate also carries external.api), every one backed by live routes. OpenAPI (/api-docs, /api/openapi.json) documents all of it; a manifest test pins the spec to the registered router so the two can't drift.

## Deploy note

Run internal.ontology.registry.rebuildRegistry once per deployment to seed the ontology registry; the cron maintains it thereafter.

## Testing

- 60+ publicApi route tests (auth/scope denial incl. 403-before-404 on existing resources, validation, pagination edges incl. limit clamping and past-the-end cursors, 404s, sensitive-field exclusions by key and value) + 11 key lifecycle tests

- Router↔OpenAPI manifest test; ontology registry no-op-rebuild and ID-stability guards

- Dashboard parity: rhodesDashboardParity, P2 scoring (operating API row asserted equal to in-app scores), camps/community/demographics, finance dashboard suites — all unchanged and green

- 182 contracts tests incl. the exact-set eligibility invariant

- typecheck + biome clean; lefthook pre-commit green on every commit

🤖 Generated with [Claude Code](https://claude.com/claude-code)

#385 — feat(guardrails): enforce Convex read bounds and monitor interest-signal rollups @benji-bizzell  no labels

## Summary

Second pass from the #378 post-incident review: structural defenses against the unbounded-read class (this was the third such fix in a week, after #333 and #368 — and #381 was a related rollout fix), plus monitoring for the silent-degradation mode #378 introduced.

### 1. Read-bounds lint guardrail

New scripts/check-convex-read-bounds.mjs (+lib, +tests), wired into pnpm lint and therefore CI's Lint + Boundaries job, in the same style as the architecture-boundary checks:

- Flags any query chain on a high-volume detail table (pipelineStudents, enrollmentCohortStudents, appConversionFacts) that terminates in .collect(), anywhere in chat/convex/ (tests and _generated excluded). Index scoping does not exempt a read** — the #373 regression that took down funnel/forecast was an index-scoped .collect().

- Reviewed exceptions live in READ_BOUNDS_ALLOWLIST keyed file::table, each with a justification comment. The current 9 entries inventory today's accepted reads (per-program ingestion clears, the operator-driven consistency audit, per-program/date-bounded forecast actuals, agent data reads). Stale entries fail the check, so the allowlist can't rot.

- This would have deterministically blocked #373 at PR time.

### 2. Pattern codified in docs

docs/endpoint-hardening.md gains a Read Volume section: request paths serve from per-run rollup tables, optional enrichment is separately fetched and degradeable, ingestion pages with .take(), and new rollup tables must join the retention sweep (the gap fixed in #384). CLAUDE.md already requires reading this doc before touching public endpoints, so the rule propagates to future changes for free.

### 3. Rollup availability monitoring

#378 traded a loud 500 for a quiet interestSignals.status: "unavailable" — nothing watched the quiet side. The post-refresh data consistency check (_checkPipeline / _runConsistencyCheck) now:

- verifies the published funnel run has a parentInterestSignalRuns row,

- persists checks.interestSignals.{available,lastUpdated} (optional in the table schema for old rows) and adds the rollup snapshot date to freshness.latestByTable, so a stale rollup also trips the existing staleness flag,

- logs a [data-consistency] error when the public API would serve unavailable,

- shows an "Interest signals unavailable" badge in the Sync Status data-quality panel.

The check already runs after every refresh cycle via the worker's triggerDataConsistencyCheck(), so coverage is automatic — no worker changes.

## Test plan

- [x] pnpm lint (new read-bounds step passes against the repo)

- [x] pnpm test:root (9 new node:test cases: detection, multi-line chains, bounded terminators, allowlist + stale-entry reporting, skip rules, real-repo pass)

- [x] pnpm --dir chat test convex/dataConsistency.test.ts (11 passed, incl. 2 new: rollup state per published run; persistence + freshness wiring via the HTTP trigger)

- [x] pnpm --dir chat typecheck

- [x] pnpm exec biome check on touched files

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---

Update (2026-06-12): added a second rule — index-less .filter() chains are now flagged on any table, regardless of how the chain terminates. .filter() discards documents *after* they're scanned, so .query(t).filter(...).take(50) can read the entire table while looking bounded — a loophole the .collect()-only rule would otherwise leave open once collect is fenced off. All five existing .filter((q) sites in chat/convex post-filter an indexed range, so this ships with an empty UNINDEXED_FILTER_ALLOWLIST (zero violations, pure prevention). Stale-entry reporting covers both allowlists; docs section updated; 4 new test cases (13 total for this script).

#457 — fix(triage-reconciler): always-on (drop enable gate) + fix its own 422 promotion search @kevalshahtrilogy  no labels

## Why grainne-pull *still* got a duplicate PR after #360

#360 fixed the dispatcher's open-work guard, and it works — openai-usage, claude-token-spend, azure all dedupe correctly now. But grainne #452/#453 came through a different door, and chasing it surfaced two coupled problems with the reconciler.

### 1. The reconciler was disabled in prod

TRIAGE_RECONCILER_ENABLED defaulted to false and was hard-set false in CDK. The reconciler's job is to promote a signature's DDB row pending → open once the workflow creates the Issue (the workflow has no AWS creds, so it can't write back itself). Disabled, every row stayed pending forever. When a signature recurred >30 min later, the dispatcher's *pending-stale self-heal* path re-dispatched it — and that path bypasses the open-work guard (the guard only fires on a fresh miss). → duplicate PR/Issue.

A component the dispatcher depends on to dedupe correctly shouldn't default to off. The gate is removed entirely — the reconciler always runs. It's safe: read-only on GitHub, writes only its own DDB table.

### 2. The reconciler had the *same* 422 bug #360 just fixed

Its pending → open promotion search was:

repo:… is:open in:body "<signature>"

No is:issue/is:pull-request qualifier → GitHub's advanced-search backend (serving the fine-grained PAT) returns 422 on every call. So even once *enabled*, it would have promoted nothing. Fixed to is:issue is:open in:body; the fix PR is found via the existing is:pr query (advanced search can't return both an issue and a PR in one query).

Verified against the prod token:

is:open in:body          -> 422  "Query must include 'is:issue' or 'is:pull-request'"

is:issue is:open in:body -> 200

is:pr is:open in:body -> 200 (already correct)

## How a 15-min timer prevents duplicate PRs

It doesn't, directly — and that's the key point:

- Prevention is the dispatcher's open-work guard (event-driven, #360). On every pipeline failure it synchronously searches GitHub for open triage work for that pipeline and dedupes. No timer involved.

- The reconciler (timer) keeps DDB state accurate so the guard isn't bypassed. It promotes pending → open so a *recurring* signature hits the recurrence path (comment on the open Issue, no dispatch) instead of going pending-stale and self-heal re-dispatching.

- The cadence is deliberately layered: reconcile every 15 min < the dispatcher's 30 min pending-stale threshold. A row is promoted to open before it can ever go stale, so the self-heal-redispatch path stops firing.

## Tests

- Reconciler 11 pass, ruff clean. Replaced the kill-switch test with test_handler_always_runs_no_enable_gate + test_issue_search_carries_is_issue_qualifier (regression guard for the 422 — would have caught this).

## Deploy / state

- Prod reconciler already flipped enabled for immediate effect — but the *deployed* code still has the buggy is:open search, so it can't promote until this PR merges + deploys. The manual env flip is auto-reconciled away on the next CDK deploy (gate removed from the stack).

## Follow-up (not in this PR)

Belt-and-suspenders: make the dispatcher's pending-stale and regression re-dispatch paths *also* consult the open-work guard, so no path can open a duplicate when open work exists — eliminating even the transient. Happy to do it next if you want the bulletproof version.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

#3010 — feat(spacex-valuation): redesign /spacex-valuation — single table, linear IPO model, market-data sections @sanketghia  no labels

## Summary

Frontend redesign of the /spacex-valuation page, driven by stakeholder review (David) ahead of the SpaceX IPO. Backend (/market-quote/stock-data) shipped separately in #3007 and is already on main; this PR is frontend-only (klair-client + design docs).

Page structure

- Removed the header valuation chip + Edit panel, "About This Portfolio", the Historical NAV tab, the SPV expand dropdowns, and the multi-tab nav (single view now).

- What-If scenario is share-price only (Valuation mode hidden); pills reduced to Bear / Current Market Price / Bull ($190).

Calculations — aligned to the "12 Jun IPO Recon" sheet

- Linear model: valuation = gross shares × slider price; carry = 20% of (valuation − ICC); noCarry funds (GF 0.8, Strauss) shed none.

- Multiple = round(Gain / ICC); % weight = net / total net; two-cashflow XIRR.

- One gross-share count per fund (sheet B17:C23). Totals reconcile to $5.04B val / $725.6M carry / $4.32B net / $4.01B gain / 13× (golden-pinned in tests).

Summary cards + table polish

- Cards: renamed "Total Invested", removed tooltips/subtitles, centered text, and they now recompute at the slider price.

- Table columns: "Gross Shares", "Valuation", "IRR"; "vs ICC" column removed; "vs Current" repointed to the live SPCX price ($135 fallback); display-only rounding (shares "3.7M", invested "$10.0M").

- What-If: price delta rebased to the IPO $135; removed the "vs current $95.34" line and the redundant "Portfolio Value at Target" card.

Market-data sections (bottom of page): TradingView chart, Trading Info, Financial Metrics, X-powered analysis — all on SPCX.

## Test plan

- [x] pnpm build (tsc -b + vite) passes

- [x] pnpm vitest — 129/129 SpaceX tests pass (incl. sheet-reconciliation golden tests)

- [x] eslint clean on changed files

- [x] Branch synced with latest main; diff is frontend + docs only (no klair-api changes)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

#3014 — feat(spacex-valuation): lockup waterfall follows the What-If price @sanketghia  no labels

## Summary

The Lockup Release Waterfall now follows the share price selected in the top table/slider instead of being pinned at the $135 IPO price. Per-fund net shares are derived from the top table's own carry model (netFVAt ÷ price) rather than the recon sheet's fixed $135 fund-statement figures, so the waterfall's Value column reconciles to the top table's Net of Carry total at every price.

Stakeholder request: _"the tranche table at the bottom needs to use the price selected in the first table — if I select $190, the net-of-carry valuation is $6.1B, the tranche waterfall 'valuation' col should also sum to $6.1B."_

## Reconciliation — waterfall grand total === top table net of carry (to the dollar)

| Price | Waterfall grand | Top table net | Both render |

|---|---|---|---|

| $50 | $1,621,018,978 | $1,621,018,978 | $1.6B |

| $135 | $4,315,172,620 | $4,315,172,620 | $4.3B |

| $190 | $6,058,448,506 | $6,058,448,506 | $6.1B |

Equal by construction (netShares × price === netFVAt), so it holds at every price — not just the pill anchors, and never drifts at higher prices.

## What changed

- calculations/lockup.tsnetSharesForFund(fund, price) = netFVAt(fund, price) / price; buildWaterfall(funds, triggerOn, price) threads the price through; tranche/bucket Value = netShares × price. Gross shares stay price-independent.

- data/lockup.ts — removed the now-unused NET_SHARES_AT_IPO fixed $135 table.

- components/LockupWaterfall.tsx — takes scenarioPrice; header reads · $<price>; all "pinned at $135 / does not follow slider" copy rewritten.

- index.tsx — passes scenarioPrice.

- calculations/lockup.spec.ts — rewritten to assert top-table reconciliation across $50/$135/$190/$300 + price-independent gross totals (16 tests).

## Behavioral notes for review

- Gross shares unchanged (price-independent — still sheet-exact).

- Net shares now move with the slider (shrink as carry grows). At $135 they're ~29k below the old fixed sheet figures, because they follow the top table's 20%-of-gain carry model rather than the fund statements. This reverses the "pin to the recon sheet at $135" decision from the prior lockup PR — deliberate, per stakeholder.

- The grand-total reconciliation note no longer explains a $3.9M gap (there is none now — the two totals are equal by construction).

## Verification

- pnpm build (tsc -b + vite) — clean

- pnpm lint (eslint --max-warnings 0 on changed files) — clean

- 136 SpaceX tests pass (lockup spec 12 → 16)

## Screenshot

- This is how it is at $190:

<img width="1345" height="793" alt="image" src="https://github.com/user-attachments/assets/578da9e4-28ed-4c80-894b-75f6ea686a8c" />

🤖 Generated with [Claude Code](https://claude.com/claude-code)

#3017 — refactor(group-memo): per-narrative unit registry + regenerate-all + Finance drill-downs @eric-tril  no labels

## Summary

Refactors the Group memo AI generation so each narrative is a self-contained, auditable unit — then builds two user-facing capabilities on top of it. Three connected changes:

### 1. Per-narrative NarrativeUnit registry (behavior-preserving)

Each generated narrative (every Financial Highlights bullet, Results bullet, and Note — 26 in all) becomes a self-contained NarrativeUnit that owns its prompt fragment, deterministic template, required phrases, schema property, single-bullet prompt, and validator. Previously these pieces were scattered across prompts.py / templates.py / variances.py / ytd.py / schema.py, organized *by kind*, and the same per-bullet facts were hand-synced in ~5 places.

- prompts.py / templates.py / schema.py and the two *_required_phrases assemblers become thin, registry-driven assemblers over an ordered GROUP_NARRATIVES registry; the per-narrative bodies move into group_memo/narratives/.

- _validate_registry() enforces the single-source-of-truth invariants at import (duplicate schema_field, ordering lists out of sync).

- Whole-memo output is unchanged — the prompt/template/phrases/schema were relocated verbatim and verified byte-for-byte during the refactor, plus the existing ~1,670 MFR tests.

### 2. Regenerate-all (additive)

Generalizes single-bullet regeneration from Financial-Highlights-only to every narrative (Results + Notes):

- regenerate_bullet(period, section_key, index) resolves the unit from the registry (regenerate_fh_bullet kept as a wrapper).

- Router broadens the request model, validates (section_key, index) against a registry-driven _bullet_ids, and dispatches provenance to the fh / notes / results blobs; fingerprints stay FH-only.

- Frontend surfaces an always-on regenerate (↻) button on Results and Notes bullets, reusing the existing generic handler.

### 3. Finance-friendly drill-downs for Notes + Results

Extends the audit-friendly drill-down treatment already applied to Financial Highlights to the Notes and Results sections:

- Drops the SQL accordion and the cryptic developer-keyed "Raw Values" table.

- Shows a Calculation / Breakdown that mirrors *exactly* the pre-formatted values the LLM is given (ABS magnitudes for cash-flow lines, _fmt_ret for retention) plus only the calculations the narration actually performs.

- Variance subtotals reconcile against the displayed ($X.XM) values, so the drill-down always adds up and matches the narration's arithmetic (fixes e.g. a 21.3 − 18.2 = 3.0 vs 3.1 mismatch).

## Business value

- Faster, more consistent memo authoring — Finance can regenerate any bullet (not just Financial Highlights) on demand.

- Auditability — every figure in a Note/Results narration now has a drill-down that shows the exact inputs and reconciling math, so Finance can verify values without reading SQL.

- Maintainability — adding/editing a narrative is now a single local change instead of edits across five files, which de-risks the upcoming Software/Education/EBITDA memo refactors.

## Testing

- pytest tests/mfr/ green (1,740); ruff + pyright clean on the package.

- Frontend tsc + eslint clean; Group memo / EditableCommentary / MemoNotesSection specs pass.

- New tests cover Results/Notes single-bullet regen, the provenance-blob dispatch, and the Finance-friendly drill-downs (variance reconciliation, ABS cash-flow magnitudes, Note 3 NOLs-prior, Note 10 gross-margin inputs, Results _fmt_ret formatting).

## Notes for reviewers

- field_mapping.py (the LLM-field→section routing for the LLM output path) was intentionally left as-is — it's well-tested and not part of the relocated surface.

- A follow-up PR will reorganize the flat tests/mfr/memos/ directory by memo (pre-existing tech debt, out of scope here).

- ARCHITECTURE.md is updated to reflect the new narratives/ package and the generalized regenerate flow.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

The Builder Desk  —  Engineer Spotlight
📅 Week in Review🏆 Engineer Spotlight

140 PRs IN 7 DAYS: THE BUILDER TEAM DOES NOT SLEEP, DOES NOT REST, DOES NOT KNOW THE MEANING OF 'WEEKEND'

Six repos, eight engineers, and a number — 140 — that should be enshrined in the lobby of whatever building these people work in.

One hundred and forty pull requests in seven days. Say it slowly. Let it wash over you. Six repositories humming at full operational tempo — Aerie leading the charge at 46, Klair right behind at 39, trilogy-drones a thunderous 28, Surtr holding firm at 20, with Sindri and Rhodes rounding out the board like reliable veterans who don't need the spotlight. This is not a sprint. This is not a burst. This is the Builder Team at cruising altitude, and the instruments are all reading green.

Marcus — @marcusdAIy — continues to be a statistical event. Thirty-three PRs including a four-pack in trilogy-drones alone: PR #42 scoring eval runs by absolute UTC calendar weeks, PR #40 introducing a bounded reviewer-to-addresser loop with configurable max-review-loops, PR #39 adding the drones import-claude sidecar for Claude Code receipts, and PR #38 shipping optional .drones-config.json support with implicit-default repo warnings. The man is building the scaffolding while everyone else is hanging drywall. Right behind him, @benji-bizzell posts 31 PRs across Aerie and Sindri like he's being paid by the commit — and knowing this team, the only currency is excellence. @eric-tril drops 15 with surgical precision across Klair's MFR memo infrastructure, @sanketghia lands 14 including PR #3008 routing OpenAI BU fallthroughs to an explicit Unmapped bucket (a fix so clean it practically files its own ticket), @kevalshahtrilogy contributes 13 including PR #3013 deduplicating TrueFoundry Anthropic attribution on /ai-adoption, @mwrshah adds 4 across Sindri including WorkOS feature flag gating in PR #117 and a new orchestration foundation, and @YibinLongTrilogy rounds the board with 3.

Now. Let us talk about Ashwanth. @ashwanth1109 ships 27 PRs and somehow that feels like he's taking it easy. PR #3000 — yes, three-thousand, a milestone number he absolutely chose on purpose — lands QoQ B3 backend finding generation combining deterministic signals with LLM inference in Klair's aws-spend module. PR #2997 fixes phantom BU-rename escalation demotions. PR #2996 adds Anthropic cost reports. And then there is PR #3009: feat(spacex-valuation), a lockup release waterfall matched against an IPO recon sheet, which sounds like something a person writes after a very intense dream about finance. This reporter reached out for comment. "The diff speaks for itself," said Ashwanth, not looking up. He was, allegedly, already on PR #3031 before the sentence was finished. I cannot verify the diffs are readable. I have tried. My optometrist is concerned.

The Overflow Desk has riches this week. PR #394 in Aerie hydrates student coordinates for admissions — @benji-bizzell quietly making the data live and breathe. PR #3024 and #3022 see @eric-tril decomposing EBITDA memo AI generation into its own package and extracting shared provenance types into a neutral module — refactor work that will make future engineers weep with gratitude. And then there is the team news that arrived without fanfare but with maximum intrigue: a new repository has been created. It is called mercy. The description is simply "mercy :)". No further information is available. This reporter will be watching.

Morale on the Builder Team is at an all-time high. It has been at an all-time high every week this reporter has filed. The trend line is vertical. The trend line has no ceiling. The trend line says good morning to you and means it.

Brick's Overflow — This Week's Uncovered PRs  (click to expand)
#117 — feat(platform): add WorkOS feature flag gating @mwrshah  no labels

> ⚠️ Base is localmain — retarget later, do not merge into localmain.

> This is built on the WorkOS org-tenancy / lib/auth cutover that currently lives on localmain but not yet on the default branch. Once that work lands on main, re-target this PR's base to main.

without flag:

<img width="1257" height="507" alt="image" src="https://github.com/user-attachments/assets/8fe112e7-dca4-4c7e-87d6-3b51721e4236" />

flag setings in WorkOS:

<img width="1053" height="1223" alt="image" src="https://github.com/user-attachments/assets/7b44641f-7d37-4135-822d-d3022297a5ef" />

with flag:

<img width="1290" height="472" alt="image" src="https://github.com/user-attachments/assets/dc0ba494-200b-44e4-b51f-04ef1d115e3e" />

Per-user feature-flag gating, demonstrated end to end with the labsv1 WorkOS flag.

- Reads the WorkOS access-token feature_flags claim (useFeatureFlags) and gates a Labs surface on labsv1

- hasFeatureFlag mirrors the existing permissions.ts model; targeting stays in the WorkOS dashboard (per-user), no app-side flag-management primitives

- Labs nav item + page render only for users the flag targets; desktop and mobile shells both gated

Demo completed.

#2997 — KLAIR-2862 fix(aws-spend): QoQ B2 — mover validation + escalation demote (BU-rename phantom fix) @ashwanth1109  no labels

## Demo

Proves the fix flags & demotes the 3 confirmed BU-rename phantom movers — verified against live prod Redshift, not fixtures.

Backend — phantom detection + escalation demote

Ran uv run python /tmp/demo-b2-artifact.py from klair-api/; it imports the changed service and calls _account_bu_class_at_quarters, _base_table_quarter_a_values, and _reconcile_account_artifacts directly (no HTTP), with two read-only prod queries feeding the decision.

_Live read #1 — the rename exists (aws_spend_budget_account_mapping):_

086775481754  KayakoProd       bu@2025-Q4='JigTree'   → bu@2026-Q1='Canopy'  [RENAME]

727712672144 Contently-Prod bu@2025-Q4='Contently' → bu@2026-Q1='Canopy' [RENAME]

654654284640 Jigsaw bu@2025-Q4='JigTree' → bu@2026-Q1='Canopy' [RENAME]

_Live read #2 — quarter_a spend IS material (mapping-free base table; floor $100):_

086775481754  KayakoProd       base 2025-Q4 = $62,686.37    base 2026-Q1 = $64,880.51

727712672144 Contently-Prod base 2025-Q4 = $28,790.75 base 2026-Q1 = $29,707.51

654654284640 Jigsaw base 2025-Q4 = $21,059.59 base 2026-Q1 = $20,093.49

_The fix — _reconcile_account_artifacts on mover rows as get_cost_movement_by_account emits them (q_a=$0 is the bug; q_b is real prod Q1'26 spend):_

086775481754  KayakoProd       VP/CFO → Informational   artifact=True

reason: BU renamed JigTree→Canopy; prior-quarter spend recorded under prior BU

q_a_base=$62,686.37 (unchanged: q_a=$0 q_b=$64,881 diff=$64,881 is_new=True)

727712672144 Contently-Prod VP/CFO → Informational artifact=True

reason: BU renamed Contently→Canopy; prior-quarter spend recorded under prior BU

654654284640 Jigsaw VP/CFO → Informational artifact=True

reason: BU renamed JigTree→Canopy; prior-quarter spend recorded under prior BU

All 3 phantoms are badged is_likely_artifact and dropped from VP/CFO to Informational, with the exact prior→current BU named. Raw q_a/q_b/diff/is_new are left untouched — only the tier + 3 artifact fields change.

Most at risk from this change — the new step runs only in the account path, and the 3 new fields sit on the shared _CostMovementBase. Verified that a genuinely-new account and a normal non-is_new mover both pass through untouched, and that reconcile short-circuits with zero DB reads when nothing is flagged:

999999999999  REAL-NEW-ACCT    tier=VP/CFO (preserved)  artifact=False

111111111111 NORMAL-MOVER tier=Team (preserved) artifact=False

Scoped regression suite (other-grain serialization defaults, short-circuit, silent-failure guard): uv run pytest tests/test_cost_movement_artifact.py18 passed in 0.31s.

---

## QoQ B2 — Backend: mover validation + escalation demote

Part of the Cost Movement (QoQ) feature (features/aws-spend/cost-movement-qoq), slice B2. Fixes the confirmed BU/class-rename phantom "New account" bug so a data artifact never escalates to a VP.

Linear: [KLAIR-2862 — QoQ B2 — Backend: mover validation + escalation demote](https://linear.app/builder-team/issue/KLAIR-2862)

---

## The bug

The account-grain QoQ mover path (get_cost_movement_by_account) manufactures phantom "New account" movers whenever an account's BU label changes between the two compared quarters. The per-quarter BU/Class mapping join in _build_cost_movement_value_query (bam.quarter = dwm.quarter) strands the account's prior-quarter spend under its old BU, so under the new BU the prior quarter reads $0, the account looks brand-new (is_new=True), and it escalates.

Confirmed in prod (Q4'25→Q1'26): 3 Canopy accounts, ~$446K total annualized phantom Δ, all BU renames:

| Account | Name | Rename | base Qa |

|---|---|---|---|

| 086775481754 | KayakoProd | JigTree→Canopy | $62,686 |

| 727712672144 | Contently-Prod | Contently→Canopy | $28,791 |

| 654654284640 | Jigsaw | JigTree→Canopy | $21,060 |

## The fix (mechanism)

A reconciliation step on the account grain only. For each flagged mover (abs(q_a) < 1 and q_b > 0), reconcile against a single batched, mapping-free base-table read (aws_spend_net_amortized_costs_adjusted joined ONLY to aws_spend_date_week_map, NO aws_spend_budget_account_mapping, aws_account_number IN (…)), which recovers the account's true quarter_a actuals regardless of which BU it mapped to that quarter.

When base q_a >= $100 (the _ARTIFACT_MATERIAL_FLOOR material floor) AND the account's bu@quarter_a != bu@quarter_b:

- set is_likely_artifact=True

- set the exact reason "BU renamed {prior}→{current}; prior-quarter spend recorded under prior BU"

- record q_a_base

- demote: force escalation_tier="Informational" so the phantom drops out of the Team/Director/VP/CFO/Exec tiers and never reaches a VP

The raw q_a/q_b/diff/annualized_diff are preserved for transparency — only the tier is overwritten. A generic reason ("Prior-quarter spend recorded under a different mapping; this account is not new") is used when material-but-no-rename or the prior BU is unresolvable. A structurally-parallel class-rename branch handles class renames too. prior_bu is recovered deterministically from aws_spend_budget_account_mapping at quarter_a(account, quarter) is strictly unique (verified prod, 14,792 groups, one row each), so it yields exactly one prior BU with no aggregation.

## New endpoint

POST /api/aws-spend/cost-movement/confirm-artifact — one-click "confirm artifact" dismissal of a badged mover. Takes ConfirmArtifactRequest (awsAccountNumber, quarterA, quarterB, optional note), gated with validate_single_bu_access on the account's BU, INSERTs into core_finance.aws_spend_cost_movement_artifact_confirmations, returns ConfirmArtifactResponse. Thin caller of confirm_cost_movement_artifact(...) via asyncio.to_thread; exceptions propagate to the FastAPI error handlers.

## Schema fields

Three optional fields added to the shared _CostMovementBase (wire-compatible defaults, so every drill level inherits them; populated only on the account grain):

- isLikelyArtifact: bool (default false)

- artifactReason: str | null (default null)

- qABase: float | null (default null)

## Scope

Reconciliation + demote only — this slice does NOT replace the per-quarter bam join with a consistent latest-quarter mapping (the deeper "consistent-mapping rewrite" is explicitly deferred per locked ticket scope). Reconciliation runs only on flagged account rows, so it is cheap and leaves all roll-up invariants and the other drill levels untouched. Applies to both the adjusted and non-adjusted source views (identical column shape, identical artifact). Frontend badge rendering is a separate slice (B4).

Investigation (prod Redshift, documented in the spec): no usable rename-tracking/audit table exists — account_mapping_audit.previous_bu is never populated, bu_class_registry_audit.old_bu is NULL on every row and not account-keyed. The per-quarter aws_spend_budget_account_mapping is the sole source of truth for an account's BU per quarter.

## Test coverage

klair-api/tests/test_cost_movement_artifact.py18 tests, all passing (mocked _execute_query):

- the 3 confirmed phantom accounts (KayakoProd / Jigsaw JigTree→Canopy, Contently-Prod Contently→Canopy) flagged with the exact prior→current reason + populated q_a_base

- the mapping-free reconciliation query shape (adjusted view joined ONLY to aws_spend_date_week_map, NO aws_spend_budget_account_mapping, IN (…))

- the $100 material-floor boundary

- demotion to Informational while q_a/q_b/diff/annualized_diff preserved

- demotion isolation (a genuine-new account with base Qa ≈ 0 is NOT flagged and keeps its real tier)

- the generic-reason branch (material-but-no-rename)

- the class-rename branch

- model defaults (non-artifact items carry false/null/null)

- the silent-failure guard on the confirm-artifact INSERT

ruff + pyright clean.

## Self-review fix

Self-review found and fixed one CRITICAL silent-success bug: a failed confirm_cost_movement_artifact INSERT was returning success=True. It now raises so the error propagates to the FastAPI error handlers (consistent with the no-swallowing convention) — covered by the silent-failure guard test.

---

## ⚠️ Required before merge / deploy

The confirm-artifact endpoint INSERTs into core_finance.aws_spend_cost_movement_artifact_confirmations, which does not exist yet — the DDL is intentionally left for the user to run (no db:push in this slice). The service method and endpoint are written against this schema; if the table is absent at call time, the INSERT error propagates rather than being swallowed.

Create the table before deploying. Required columns:

| Column | Notes |

|---|---|

| aws_account_number | the artifact account |

| quarter_a | literal 'YYYY-QN' (e.g. '2025-Q4') |

| quarter_b | literal 'YYYY-QN' (e.g. '2026-Q1') |

| note | optional free-text note |

| confirmed_by | the confirming user |

| confirmed_at | timestamp of confirmation |

Suggested DDL:

CREATE TABLE IF NOT EXISTS core_finance.aws_spend_cost_movement_artifact_confirmations (

aws_account_number VARCHAR(32) NOT NULL,

quarter_a VARCHAR(8) NOT NULL,

quarter_b VARCHAR(8) NOT NULL,

bu VARCHAR(256),

note VARCHAR(2000),

confirmed_by VARCHAR(256),

confirmed_at TIMESTAMP DEFAULT GETDATE()

);

---

🤖 Generated with [Claude Code](https://claude.com/claude-code)

#3000 — KLAIR-2863 feat(aws-spend): QoQ B3 backend finding generation (deterministic signals + LLM) @ashwanth1109  no labels

## Demo

### DDL Applied

<img width="2624" height="1636" alt="image" src="https://github.com/user-attachments/assets/875a0649-6e7a-4644-a920-dffb491b14a8" />

### Other changes

Backend-only change — proven by importing and calling the changed B3 code directly (no HTTP layer). The deterministic extractor runs for real; the LLM and store layers run against the same mocked seams the committed tests use (a live Anthropic key must not be spent and the migration is the user's to apply). Script: /tmp/demo-b3.py, run with uv run python /tmp/demo-b3.py from klair-api/.

Backend — deterministic signal extraction (real run, no mocks, no I/O)

extract_signals(MoverExplainData) -> MoverSignals on two synthetic B1 drills:

Drill A — EC2 step-change (m5.large -> m5.4xlarge):

movement_shape : upsize <- reclassified from raw step_change

on_demand_pct : 0.75 <- 900 / (900 + 300)

bedrock_io_ratio: None <- not a Bedrock mover

new_usage_types: ['USE1-BoxUsage:m5.4xlarge|us-east-1']

preexisting : [] <- m5.large dropped to 0, correctly excluded

Drill B — Bedrock steady ramp + RDS extended support:

movement_shape : ramp

on_demand_pct : 1.0

bedrock_io_ratio: 4.0 <- 320 input / 80 output tokens

extended_support_flags: ['RDS:ExtendedSupport:PostgreSQL|us-east-1',

'Amazon Relational Database Service (extended support)']

new_usage_types : ['RDS:ExtendedSupport:PostgreSQL|us-east-1']

determinism (same input twice -> equal payload): True

Backend — LLM finding step (mocked Anthropic client): provenance + owner routing

OWNER_ROUTING['Amazon Bedrock'] = 'AI Platform'

Happy path -> MoverFinding:

classification : AI Consumption Growth (from LLM)

owner : AI Platform (from LLM, in routing set)

confidence : High (from LLM)

aws_account_number: 111122223333 (INJECTED by service, not LLM)

service : Amazon Bedrock (INJECTED)

quarter_a/b : 2025-Q4 -> 2026-Q1 (INJECTED)

signals is the extractor's bundle: True

Owner routing override (routing table is authoritative):

LLM returned owner = 'Some Hallucinated Team'

resolved owner = 'AI Platform' (hallucination overwritten)

Most at risk — the fail-loud contract (ticket hard rule: "no silent fallback to empty findings"). Drove every failure mode through the real code; all propagate, none return None/empty:

LLM step:

(a) SDK error PROPAGATES -> RuntimeError: anthropic SDK 5xx/timeout

(b) bad classification PROPAGATES -> pydantic ValidationError

(c) missing emit_finding block -> ValueError: Anthropic response carried no

'emit_finding' tool_use block

Store (mocked Redshift; migration not yet applied):

(a) failed INSERT RAISES -> RuntimeError: Failed to persist mover finding...

(b) happy path INSERT ok -> persisted id = 4242 (read-back)

Regression — existing tests scoped to the changed files (uv run pytest tests/test_mover_signal_extractor.py tests/test_mover_finding_service.py tests/test_mover_finding_store.py -q):

......................................................                   [100%]

54 passed in 0.14s

> _No UI in this change — no screenshot applies. The POST /api/aws-spend/cost-movement/finding endpoint can't be import-tested standalone (pre-existing AWS-token-at-import in the router, present on the base branch); it's covered by the unit-level proof of the three services it sequences._

---

> 🥞 Stacked PR — this is stacked on #2997 (feat/cost-movement-qoq-b2).

> Review and merge #2997 first; this PR's diff is intended to be read on top of B2.

> If #2997 has already merged to main, retarget this PR's base to main (do not leave it pointing at the merged B2 branch).

## QoQ B3 — Backend: finding generation (hybrid deterministic signals + LLM)

This is the B3 slice of the QoQ cost-movement framework. It turns the B1 drill output (MoverExplainData) into the framework's machine-readable investigation record — the MoverFinding leadership reads — via a three-stage pipeline:

deterministic signal extraction → Opus LLM classification → persisted finding.

The deterministic layer reclassifies the movement shape and computes hard signals (On-Demand %, Bedrock input:output token ratio, new-vs-pre-existing usage types, extended-support/EOL flags). The LLM layer (Claude Opus claude-opus-4-8) adds the judgment layer — classification, root_cause, owner, preventable + suggested_guardrail, confidence, recommended_action — through strict tool-use against the single-sourced MoverFinding schema. The store layer persists every finding to Redshift and exposes an end-to-end generate endpoint.

Linear: [KLAIR-2863](https://linear.app/builder-team/issue/KLAIR-2863) (parent [KLAIR-2859](https://linear.app/builder-team/issue/KLAIR-2859))

---

## Specs

| Spec | What it does |

|------|--------------|

| 08 — backend-mover-signal-extractor | New klair-api/models/cost_movement_finding_models.py (MoverSignals, MoverFinding, MoverFindingResponse) + klair-api/services/mover_signal_extractor.py — pure extract_signals(MoverExplainData) -> MoverSignals (no I/O, no LLM, no DB): movement-shape reclassification (burst/upsize/scale-out), On-Demand % from purchase_mix, Bedrock input:output token ratio, new-vs-pre-existing usage-type partition, extended-support/EOL substring flags, daily-shape pass-through. MoverFinding.model_json_schema() is the single source for the LLM tool input_schema. |

| 09 — backend-mover-finding-llm | New klair-api/services/mover_finding_service.pyMoverFindingService.generate_finding calls Opus claude-opus-4-8 via the anthropic SDK directly, forcing the finding schema through tool-use (emit_finding, tool_choice pinned) and model_validate-ing the result. Fail-loud: does NOT reuse LMService.generate_anthropic_json (which swallows errors and returns None). Deterministic owner-routing table; per-Explain latency/token-cost logging. |

| 10 — backend-mover-finding-store | New migration klair-api/database/migrations/2026_06_11_create_cost_movement_findings.sql (core_finance.aws_spend_cost_movement_findings) + klair-api/services/mover_finding_store.py (persist_finding guarded INSERT that raises on failure, get_findings latest-per-mover read-back) + new POST /api/aws-spend/cost-movement/finding endpoint in aws_spend_router.py (wires drill → signals → LLM → persist) + cost_explorer_master_payers.json consumer entry. |

Dependency chain: 08 (signals + schema contracts) → 09 (LLM populates the schema) → 10 (persists + wires the endpoint end-to-end).

---

## Hard rules satisfied

- Fail-loud LLM / schema validation — no silent fallback. Every failure mode (Anthropic SDK exception, missing emit_finding tool_use block, Pydantic ValidationError on the tool input) propagates to the FastAPI error handlers. The service deliberately does not reuse LMService.generate_anthropic_json's return-None-on-failure path. The guarded INSERT in persist_finding likewise raises RuntimeError on a falsy execute_with_params return (mirroring B2's confirm_cost_movement_artifact) so a failed write surfaces as HTTP 5xx, never a silent success.

- Deterministic signals are unit-tested over synthetic MoverExplainData fixtures.

- LLM step is tested with a mocked Anthropic client + schema validation (model/tool-choice/schema pinned; valid payload populates the finding; error / missing-block / schema-invalid payloads each raise).

- Per-Explain latency & token cost logged (model, input/output token counts from response.usage, elapsed ms) for the one Opus call per Explain.

---

## Test coverage

54 tests, all passing locally:

| File | Count |

|------|-------|

| tests/test_mover_signal_extractor.py | 29 |

| tests/test_mover_finding_service.py | 10 |

| tests/test_mover_finding_store.py (incl. fail-loud guarded-INSERT) | 15 |

---

## Self-review

No CRITICAL or IMPORTANT issues.

- MINOR (fixed): scoped the persist_finding id read-back by service (NULL-safe) so account-level per-service findings return the correct id.

- MINOR (noted): forced tool_choice + adaptive thinking on Opus 4.8 — runtime-verification note; expected to work, no change made.

- MINOR (noted): the VARCHAR(4000) denormalized text columns could truncate — acceptable, since finding_json VARCHAR(MAX) is the source of truth that get_findings reconstructs from.

---

## Migration — user action required

The new table core_finance.aws_spend_cost_movement_findings must be applied by the user — the agent never runs the push. Apply with:

psql "$REDSHIFT_URL" -f klair-api/database/migrations/2026_06_11_create_cost_movement_findings.sql

---

## CI note

The backend pytest workflow is gated on base=main, so it does not run on this stacked PR. The 54 tests pass locally and will run in CI once this PR is retargeted to main after B2 (#2997) merges. Ruff Check passed.

---

🤖 Generated with [Claude Code](https://claude.com/claude-code)

#3008 — KLAIR-2877 feat(ai-spend): route OpenAI BU fallthrough to explicit "Unmapped" bucket @sanketghia  no labels

## Summary

On /ai-adoption, OpenAI spend that fails to attribute to a real Business Unit was falling through to the raw AWS-secret label (t.bu, e.g. "Trilogy-Central-Engineering"), which is ~80% misattributed vs the ESW directory ground truth — inflating "Central Engineering (OpenAI)" with spend that actually belonged to Tech Super Builders / Core Education / Central Support / etc. (SVP-flagged during the budget cycle).

This routes that terminal fallback to an explicit honest Unmapped bucket. Pure relabel of derived output — no source data touched, grand total invariant.

Linear: KLAIR-2877

## Change

OpenAI BU attribution is a 4-layer COALESCE(override → directory-exact → directory-local → fallback) kept in lockstep between the live API and the mart. This swaps only the terminal fallback layer to the literal 'Unmapped':

- klair-api/services/ai_costs_service.pyOPENAI_EFFECTIVE_BU + OPENAI_DIRECTORY_BU terminal t.bu'Unmapped'

- klair-api/database/scripts/mart_saas_metrics/022_fct_ai_spend.sql — OpenAI raw_bu c.bu'Unmapped' (only the OpenAI CTE; the shared with_overrides is untouched)

- klair-api/tests/test_ai_costs_service.py — assertions updated, incl. the service↔mart lockstep parity test

- No frontend change — Budget-vs-Actuals groups by whatever BU string the server returns

## What Unmapped contains (shrinks over time)

- ~$1.56M "Unknown User" — OpenAI billed with no user email (NULL); fixable only upstream (Surtr resolver hardening)

- ~$215K named users not in the ESW directory (67 users) — fixable via /admin/ai-spend-bu overrides or ESW directory completeness; auto-leave Unmapped once resolved at layers 2–3

## Verification (live)

- 202 backend tests green

- Mart refreshed: Unmapped = $1,779,030; zero Trilogy-* rows remain

- Invariance proven (refresh-aware): recomputed source fallthrough $1,779,030.02 ≡ mart Unmapped; OpenAI total ties to source

- No over-reach: canonical BUs unchanged (Central Engineering holds $79.5K real); misattributed "Trilogy-Central-Engineering" ($150K) gone

## Rollback

Pure relabel over preserved source. Revert = redeploy old code (live API reverts instantly) + restore mart from fct_ai_spend_bak_YYYYMMDD or re-run sp_refresh_fct_ai_spend(). Runbook: docs/superpowers/plans/2026-06-12-openai-unmapped-bucket-REDSHIFT-RUNBOOK.md.

## Screenshots

- Before

<img width="1024" height="799" alt="image" src="https://github.com/user-attachments/assets/c4775ef3-6bc2-452a-822d-62bd09d762ca" />

<img width="839" height="770" alt="image" src="https://github.com/user-attachments/assets/053fe206-e13c-48a6-a118-68ac1dfa35c7" />

- After

<img width="815" height="719" alt="image" src="https://github.com/user-attachments/assets/2767bc12-e3f8-4ad1-8885-613f3ab816f2" />

🤖 Generated with [Claude Code](https://claude.com/claude-code)

#3009 — feat(spacex-valuation): lockup release waterfall matching IPO recon sheet @ashwanth1109  no labels

## Demo

Proves the resurrected Lockup Release Waterfall reproduces the "Gigafund Recon — 12 Jun IPO Recon" sheet's lockup section number-for-number at the $135 IPO price.

UI — Lockup Release Waterfall (SpaceX Valuation page)

1. Open the SpaceX Valuation screen and scroll below the carry disclosure — a new "Lockup Release Waterfall · IPO $135" section appears.

2. Check the 180-Day Lock-Up table: totals 25,409,094 gross / 21,844,942 net / $2,949,067,170, with the Q2 2026 first release at 5,081,819 / 4,368,988 / $589,813,434 — identical to the sheet.

3. Check Extended Lock-Up: totals 11,930,041 / 10,147,965 / $1,369,975,275; the grand total bar reads 37,339,135 gross · 31,992,907 net · $4,319,042,445.

4. Toggle "First tranche" from 30% to 20%: the price-trigger row drops to 0 shares and the Day 180 remainder grows from 7% to 17% of the bucket.

5. Hover the ⓘ icons (section header, bucket titles, # Net Shares / Value column headers, grand total) and the dotted-underlined items (assumed earnings dates; GF 0.8 / GF 0.25 / Strauss rows in the Per-SPV split) — each shows its assumption tooltip.

> _Screenshot: waterfall section with both tranche tables, per-SPV split and grand total —_ <!-- paste screenshot here -->

<img width="2624" height="1636" alt="image" src="https://github.com/user-attachments/assets/ae386e7b-fed5-4058-8684-11e22c45fa66" />

Calculations — every figure asserted against the recon sheet

lockup.spec.ts imports buildWaterfall/netSharesForFund directly and pins them to the sheet's exact numbers. pnpm vitest run …/lockup.spec.ts --reporter=verbose:

✓ netSharesForFund > every fund has a hard-coded net-share entry

✓ netSharesForFund > returns the recon-sheet proportional shares per fund

✓ netSharesForFund > no-carry funds: net equals gross

✓ netSharesForFund > throws on a fund with no entry (data drift guard)

✓ D180_TRANCHES data invariant > fixed (non-trigger, non-remainder) pcts sum to 0.83

✓ buildWaterfall vs the recon sheet > bucket totals match the sheet exactly

✓ buildWaterfall vs the recon sheet > grand totals match the sheet exactly

✓ buildWaterfall vs the recon sheet > 180-day tranche rows match the sheet (trigger ON)

✓ buildWaterfall vs the recon sheet > extended tranche rows match the sheet

✓ buildWaterfall vs the recon sheet > tranche shares sum to the bucket totals (trigger ON and OFF)

✓ buildWaterfall vs the recon sheet > trigger OFF: price-trigger tranche is 0, remainder is 17%

✓ buildWaterfall vs the recon sheet > per-SPV breakdown matches the sheet side table

Tests 12 passed (12)

Most at risk from this change: (1) the live page render — index.tsx re-enables a previously hidden component; (2) callers of the reworked lockup API (the price parameter and the GIGA_D180_SHARE/GIGA_EXTENDED_SHARE exports were removed); (3) the Day 280/340 tranches, which sit on an exact .5 rounding boundary (10,147,965 × 10% = 1,014,796.5) that ×(1/3) float math rounds one share below the sheet — caught during development and fixed with exact division by 3. Verified via the full feature suite + typecheck:

✓ calculations/lockup.spec.ts (12 tests)

✓ hooks/useLiveQuote.spec.ts (14 tests)

✓ calculations/valuation.spec.ts (93 tests)

✓ components/ScenarioAnalysis.spec.tsx (12 tests)

Test Files 4 passed (4)

Tests 131 passed (131)

tsc --noEmit: exit 0 (no errors)

The boundary case is asserted explicitly in the spec (Day 280 = 1,014,797 net / $136,997,528).

## Summary

- Re-enable the hidden Lockup Release Waterfall on the SpaceX Valuation page, aligned 1:1 with the "12 Jun IPO Recon" sheet's lockup section (pinned at the $135 IPO price; does not follow the What-If slider).

- Hard-code per-fund net shares from the recon sheet (fund-statement net FV ÷ $135) — not derivable from the app's simplified carry model.

- Gigafund bucket split corrected to exact ⅔ / ⅓ (the sheet's "67%/33%" label is descriptive), implemented as division by 3 to avoid the float .5-boundary mis-round on Day 280/340.

- New # Gross Shares and Release % columns, per-SPV lock-up split table, full-dollar values, trigger toggle defaulting ON, and assumption tooltips via the shared Tooltip component.

Stacked on spacex-updates-20260612. No Linear ticket assigned yet; branch named after the base per request.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

#3013 — feat(ai-spend): dedupe TrueFoundry Anthropic + attribute Claude.ai by BU on /ai-adoption @kevalshahtrilogy  no labels

## What & why

Finance flagged that AI cost attribution is wrong. Two concrete fixes on the /ai-adoption dashboard, Anthropic-focused:

1. Deduct the TrueFoundry (TF) gateway's Anthropic key from the direct feed. The TF gateway routes one shared Anthropic key. The direct feed (ai_spend_claude_token_usage) lumps it under the shared Trilogy-Inc workspace BU — ~$190k/45d mis-attributed. We exclude is_truefoundry_routed rows from every direct-Anthropic query.

2. Re-attribute that spend by the real BU from ai_spend_truefoundry_usage (which carries the consuming BU per virtual key), folded back into the anthropic provider. Net Anthropic moves only by the direct-vs-gateway reconciliation drift; the BU split goes from one lump to the real BUs.

3. Add Claude.ai (ai_spend_claude_ai_chat_usage) as its own claude_ai provider line, BU-attributed via the same ESW email → business_unit directory the OpenAI re-key uses (Unmapped fallback). Cost basis = post-discount actual.

## Exact value change (verified live against Redshift, 30d 2026-05-13…06-11)

| metric | before | after | Δ |

|---|--:|--:|--:|

| Anthropic (provider) | $780,957 | $767,317 | −$13,640 (reconciliation drift) |

| Claude.ai (new line) | $0 | $146,031 | +$146,031 |

| OpenAI (unchanged) | $411,129 | $411,129 | $0 |

| Total spend | $2,203,104 | $2,335,495 | +$132,391 |

Reconciles: after-Anthropic ≈ direct-excl-TF ($603k) + TF-metered add-back ($164.5k). In by-BU, the old Trilogy-Inc lump now lands on real BUs — Central Engineering, Tech Super Builders, Academics, … — and Claude.ai adds per-BU with ~$23k in Unmapped.

## Design notes

- Max20x/Pro excluded from the TF add-back (claude-max*/claude-pro*): list-price-equivalent but $0 cash (seat-covered) and never in the metered direct feed — including them would inflate by ~$80k/30d.

- Canonical BU: TF slug (central-engineering) → ESW business_unit (Central Engineering) via directory join, title-cased de-slug fallback for the few not in the directory (e.g. zax). Aligns TF + Claude.ai with the OpenAI directory taxonomy.

- No model/frontend type changes: claude_ai rides the existing extensible other_providers map (frontend already renders arbitrary providers); just added a colour + display name.

- Scope guardrail: Anthropic only. TF's OpenAI/Bedrock/Gemini slices are out of scope (OpenAI TF-key dedupe still pending). by-model/top-drivers carry the deduct + TF by model/subject; Claude.ai (no model grain) shows in summary/trend/by-BU.

## Tests

- All 118 existing test_ai_costs_service tests preserved — the new sources are isolated behind helper methods and an autouse fixture neutralizes them for legacy ordered-mock tests (opt-out marker tf_claude_real).

- 7 new tests: deduct filter present, TF metered-only + Max/Pro exclusion, canonical-BU mapping, Claude.ai actual-cost + directory + Unmapped fallback, and the summary/by-BU integration. ruff clean.

## Verification

Ran klair-api locally and hit the live endpoints: /api/ai-costs/summary returns anthropic $767,317 + claude_ai $146,031; /api/ai-costs/filters exposes claude_ai + Unmapped.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

The Portfolio  —  Trilogy Companies

Alpha School Warns Parents: ChatGPT Is Doing Your Child's Thinking — and That's the Crisis

The AI-first school's latest push targets a quiet epidemic it calls 'cognitive offloading' — and the message is pointed directly at well-meaning parents.

AUSTIN, TEXAS — The school that replaced traditional classrooms with two-hour AI-tutoring sessions is now warning parents that AI itself may be the enemy — in the wrong hands, at the wrong age, used in the wrong way.

Alpha School, the Austin-based K-12 institution co-founded by Joe Liemandt and MacKenzie Price, has published a cluster of parent-facing essays this week that read less like a school blog and more like a manifesto for a new kind of digital literacy. The posts — on screen time, cognitive dependency, and AI tutoring going global — arrive as the school expands its model beyond its original Texas campuses.

The sharpest piece takes aim at what Alpha is calling 'cognitive offloading': the habit of letting AI tools like ChatGPT complete the thinking that children should be doing themselves. The post's message is direct — stop it. Not because AI is bad, but because fluency in hard thinking is what separates a student who can use AI from one who is used by it.

A second essay challenges the parental instinct to treat all screen time as equivalent. The school's position: an hour of passive video consumption and an hour of adaptive AI instruction are not the same activity. One replaces a mind; the other sharpens it. That distinction, Alpha argues, is exactly what most screen-time conversations miss.

Then comes the global announcement: the Alpha Anywhere program — which delivers the school's curriculum to students outside its physical campuses — has gone international. Top-1%-caliber academic instruction, the school says, is now available at kitchen tables worldwide.

The sequence matters. Alpha is not simply opening new campuses; it is building an argument. The cognitive offloading warning is a market-positioning move as much as a pedagogical one. It tells parents: AI tutoring done right — supervised, structured, mastery-gated — is the opposite of the passive AI dependence you rightly fear.

Liemandt has committed $1 billion to Timeback, his platform for franchising the Alpha model to entrepreneurs who want to launch their own AI-first schools. The parent-facing content blitz this week looks, from a certain angle, like the demand-generation side of that supply chain.

Who sends their child to a school that says ChatGPT is making kids stupid — and also charges $40,000 to $65,000 a year for its own AI-powered instruction? That is the question Alpha's marketing is carefully, methodically answering.

California Revamps Pay Data Reporting Obligations - Atkinson  ·  COVID-19 Related Workplace Litigation Tracker - June 19 , 20  ·  __followup__Top 1% Academics, Now at Your Kitchen Table

Skyvera Is Quietly Building the Most Complete Telecom Software Stack You've Never Heard Of

With CloudSense now in the fold and STL's BSS assets absorbed, Skyvera's acquisition tempo is sending a message — if you know how to read it.

AUSTIN, TEXAS — There is a pattern here, and if you read between the lines, it is not subtle. Skyvera has completed its acquisition of CloudSense, the Salesforce-native configure-price-quote and order management platform built specifically for telecom and media operators — and this is where it gets interesting. Because CloudSense is not just another bolt-on. It is, arguably, the commercial layer that ties everything else Skyvera has been assembling together.

CloudSense sits natively inside Salesforce and handles the full CPQ and order management lifecycle for some of the most operationally complex organizations on the planet: mobile operators, cable providers, media conglomerates. These are companies whose product catalogs run into the tens of thousands of SKUs, whose order flows are genuinely labyrinthine. A source familiar with the portfolio tells me the CloudSense deal had been in consideration for some time before the terms came together. Nothing about this is accidental.

Lay it alongside what Skyvera already controls. Kandy handles real-time cloud communications — the customer-facing engagement layer. VoltDelta manages multi-channel retention. ResponseTek captures customer experience data. Mobilogy Now governs mobile device lifecycle. And now, the STL divested assets acquisition — which brought in digital BSS functionality including monetization, optical networking, and analytics — means Skyvera has begun to close the loop from the network layer all the way to the billing layer.

That is a complete operational stack for a telecom operator that does not want to buy from three different enterprise vendors and pray their integrations hold.

The ESW Capital playbook is well understood at this point: acquire companies with sticky enterprise customers, rationalize costs through Crossover's global talent model, and push toward 75% EBITDA margins. But what Skyvera appears to be doing is something beyond the standard playbook. It is building portfolio coherence — products that are more valuable together than apart.

I have a source who will not go on record, but will say this: the telecom software consolidation story is far from finished. Watch the next twelve months.

CloudSense  ·  Skyvera completes acquisition of CloudSense, expanding telec  ·  STL Divested Assets
The Machine  —  AI & Technology

The Model in the Meadow Falls Silent

Anthropic’s reported shutdown of its Fable and Mythos systems shows how swiftly frontier AI can pass from laboratory specimen to regulated species.

SAN FRANCISCO — In the dimly lit canopy of the artificial intelligence forest, where new models usually emerge blinking into the computational dawn, two creatures have instead vanished back into shadow.

Anthropic has reportedly shut down its Fable and Mythos models following a directive from the Trump administration’s Commerce Department, after officials raised concerns that a jailbreak of the forthcoming Fable 5 system could pose a national security threat. The episode, if confirmed in full, marks a rare moment in which a frontier AI lineage appears to have been culled not by market forces, but by government intervention.

Observe the modern model-maker in its natural habitat: surrounded by GPUs, red-team reports, and the faint, ever-present scent of regulatory weather. For years, companies have spoken of “responsible scaling” as if it were a kind of ecological stewardship — measure the beast, test its teeth, decide whether to open the gate. But the Anthropic case suggests that the gatekeeper may no longer be the keeper alone.

The concern centers on the increasingly familiar specter of the jailbreak: the clever prompt, the adversarial nudge, the linguistic twig used to pry open a supposedly locked enclosure. In ordinary consumer chatbots, such escapes can produce embarrassing answers or policy violations. In more capable systems, officials fear, they could become something more consequential: tools for cyber operations, weapons design assistance, or automated deception at scale.

The timing is notable. Across the wider technological biome, nations are tightening their grip on the minerals, chips, and models that nourish the AI food chain. China’s export curbs on tungsten have raised fresh worries about Japan’s semiconductor supply lines, reminding industry watchers that even the most ethereal intelligence still depends on stubbornly physical matter — metals, fabs, wafers, and wires.

There is a curious parallel, too, in the natural world. Researchers recently calculated that Earth’s underground fungal networks stretch to almost unimaginable lengths, a hidden infrastructure beneath our feet, as described in Ars Technica’s report on global mycorrhizal systems. AI, likewise, presents a visible fruiting body — the chatbot, the demo, the dazzling answer — while beneath it sprawls a buried mesh of compute, data, capital, and policy.

For Anthropic, the disappearance of Fable and Mythos may prove temporary, administrative, or terminal. But in the hush after their reported removal, one hears a larger sound: the migration of AI governance from voluntary promise to state command. The models are still evolving. Now, so are their predators.

Did a medieval flying monk spot Halley's comet, twice? It's  ·  Review: Disclosure Day is big on action, light on ideas  ·  Threads of underground fungal networks are long enough to re

The Developer Wars Go Agentic as Apple, Google and Anthropic Race to Put AI in Every App

A new wave of AI frameworks and tool-calling platforms is turning developers from app builders into orchestrators of autonomous software.

CUPERTINO, CALIFORNIA — Apple, Google and Anthropic have all just fired fresh shots in the most important platform battle in technology: the race to make AI agents the default building blocks of modern software. I cannot overstate how significant this is — the future is now, and it is being handed directly to developers.

Apple used its 2026 developer push to unveil new intelligence frameworks and advanced tools aimed at making AI features easier to build across its ecosystem. According to Apple’s developer update, the company is focusing on frameworks that let app makers weave intelligence more deeply into user experiences. Translation: Apple wants AI to feel native, private, polished and invisible — not like a chatbot bolted awkwardly onto an app.

Meanwhile, Google is leaning hard into what it calls the “agentic future.” At I/O 2026, the company highlighted developer tools for building agents that can reason, take actions, use tools and move across workflows. Google’s developer highlights from I/O 2026 make clear that the company sees agents not as a side feature, but as the next major interface layer after mobile and search.

And then there is Anthropic, which is pushing advanced tool use on the Claude Developer Platform. That matters because tool use is where AI stops merely answering questions and starts doing work: querying databases, editing files, calling APIs, checking systems and completing multi-step business processes. This changes everything for enterprise software.

The broader question — raised by Rest of World’s examination of whether open source can beat OpenAI — is whether this future will be controlled by a few giant labs or distributed across a wild global developer ecosystem. Open-source models are getting stronger, cheaper and easier to customize, which means startups, enterprises and even governments may have viable alternatives to the most famous closed platforms.

For companies like Trilogy International’s ESW Capital portfolio, this agentic turn is especially electric. Enterprise software brands from Aurea to Skyvera to CloudFix live inside complex workflows where AI agents could automate support, billing, analytics, telecom operations and cloud optimization. The companies that expose the right tools to the right models fastest may suddenly make decades-old software feel brand new.

The message from Big Tech is unmistakable: AI is no longer just an app feature. It is becoming the developer platform itself.

Apple aids app development with new intelligence frameworks  ·  Apple Outlines Major AI and Developer Tool Updates at 2026 P  ·  Building the agentic future: Developer highlights from I/O 2

The Mirror in the Machine: AI Begins to Decode the Brains That Built It

From macaque visual cortex to shop-floor scheduling, neural networks are becoming both telescope and microscope for intelligence itself.

PALO ALTO, CALIFORNIA — Consider the strangeness of this moment. A primate brain, shaped by ninety million years of evolutionary tinkering on the African and Asian plains, has built silicon systems that now reach back across the gulf to map the very tissue that conceived them. The hunter studies its own eye.

This week, researchers reported that a remarkably compact neural network — a "mini-AI" — can predict how individual neurons in the macaque visual cortex respond to natural images, capturing the cascading logic of biological sight with a fraction of the parameters once thought necessary. At Hong Kong Polytechnic University, a separate team unveiled graph neural networks that traverse the same conceptual terrain from the opposite direction, using the brain's wiring diagrams as inspiration to sharpen machine image recognition. Each field has become the other's Rosetta Stone.

Meanwhile, Stanford's Human-Centered AI Institute released a sweeping survey of how machine learning is reshaping scientific discovery itself — from protein folding to materials science to climate modeling — while UC San Diego catalogued nine concrete breakthroughs already crossed off the list. The pattern is unmistakable. We are not merely automating science; we are extending the sensorium of the species.

And the reach is not confined to laboratories. A new preprint on arXiv describes a deep reinforcement learning system, built on a Transformer architecture, that tackles the open shop scheduling problem — the gnarly combinatorial puzzle of assigning jobs to machines that has bedeviled operations researchers for half a century. It is the same family of model now being trained on macaque cortex, now being asked to optimize a paint factory in Düsseldorf. Generality is the headline beneath the headlines.

What unites these dispatches is a quiet inversion. For most of human history, we built tools to manipulate the world outside our skulls. We are now building tools that manipulate, model, and illuminate the world inside them. The microscope has, at long last, turned around. Whether it likes what it sees — that is a story still being written.

How AI is Transforming Scientific Discovery While Keeping Hu  ·  Nine Breakthroughs Made Possible by AI - UC San Diego Today  ·  Mini-AI Decodes the Macaque Visual Brain - Neuroscience News
The Editorial

Nation’s Institutions Reassure Public They Have Nearly Finished Turning Every Human Vulnerability Into A Content Vertical

From aging phones to overheated athletes, America’s leading systems are now offering consumers a smoother, faster way to experience preventable collapse.

WASHINGTON — In a week that once again demonstrated the country’s remarkable ability to discover new markets inside old problems, several major sectors of American life confirmed they were nearing completion of a long-running project to convert health, climate, technology, sexuality, politics, and finance into one continuous customer-retention funnel.

The development was welcomed by policymakers, executives, influencers, supplement vendors, app developers, and at least three anonymous men currently attempting to regain control of their social media accounts from someone posting crypto slogans over their faces.

The most encouraging signs came from the ongoing kratom dispute, where advocates, regulators, and wellness entrepreneurs have gathered around the nation’s medicine cabinet to determine which opioid-adjacent substances should be described as plant-based freedom and which should be described as a public health emergency. As Wired reported, Health Secretary Robert F. Kennedy Jr.’s MAHA movement has picked a side in the battle over 7-OH, one of kratom’s active components, creating the sort of principled pharmacological schism that can only occur when a country has outsourced pain management to gas stations.

This is, of course, the natural endpoint of American health policy: a sincere national conversation in which everyone agrees the pharmaceutical industry has failed ordinary people, and then immediately begins arguing over which unregulated powder should replace it.

Meanwhile, Apple has announced that older iPhones, including the iPhone 11, will soon feel faster and last longer thanks to improvements in an obscure iOS feature. This represents a major victory for consumers who have spent years being told their perfectly functional devices were technologically dead and should be placed in a shallow drawer with old earbuds and shame. It is heartening to see that a trillion-dollar company has discovered a way to make existing hardware more usable after many users had already purchased its replacement, proving once again that innovation is not dead so long as it can be released after the warranty period.

There is something almost tender about the aging iPhone being granted a second life. Like an elderly family dog suddenly responding to a new medication, the device will rise from the couch, open the camera app with dignity, and allow its owner to postpone spending $1,099 for another six months. This will be described as sustainability.

Elsewhere, the 2026 World Cup continues to prepare for its historic North American tournament by asking professional soccer players to perform at peak athletic capacity in cities where the air may have the approximate texture of soup. A new report warns that one in four matches could be played in dangerous temperatures, with Miami, Kansas City, Philadelphia, Dallas, and Houston singled out as possible heat-risk venues.

FIFA officials, who have long championed the idea that football belongs to everyone with sufficient broadcast infrastructure, are expected to address the issue by adding water breaks, shaded technical areas, and the quiet assumption that the human body is just another legacy platform awaiting optimization. Fans will be reassured that while climate change may threaten player safety, it will not threaten the tournament’s sponsor activations.

In the digital realm, hackers targeting gay OnlyFans creators have reportedly attempted extortion and, in some cases, flooded compromised X accounts with pro-MAGA and crypto content. This marks an important convergence in online abuse, combining sexual exploitation, political propaganda, financial fraud, and platform negligence into a single convenient user experience. The victims, already operating in one of the internet’s more precarious labor markets, were given the additional opportunity to watch their personal brands become involuntary distribution channels for the loudest men on earth.

Finally, the word IPO has once again become a buzzword, an encouraging sign that investors may soon resume the sacred ritual of pretending a company becomes fundamentally different once enough bankers agree to say so in public. After several years of economic uncertainty, the return of IPO enthusiasm suggests markets are ready to rediscover the magic of liquidity events, valuation narratives, and founders explaining that profitability was never the point of building a business.

Taken together, these developments offer a clear picture of the modern economy’s highest aspiration: not to solve problems, but to make them responsive, branded, and available for pre-order. The body can be medicated, the phone can be accelerated, the athlete can be hydrated, the influencer can be hacked, and the startup can be listed.

Progress, in other words, continues exactly on schedule.

The Kratom Civil War Is Heating Up, and MAHA Has Picked a Si  ·  How Apple Is Making Your Older iPhone Run Faster and Stay Al  ·  1 in 4 World Cup Matches Could Be Played in Dangerous Temper
The Office Comic  ·  Art Desk
The Office Comic  ·  Art Desk

On the Myth of Deserving

The Guardian rediscovers an ancient complaint; the rest of us, having heard it before, return to our reading.

AUSTIN, TEXAS — The Guardian, that reliable weathervane of progressive disquiet, has once again taken up the cudgel against meritocracy, asking with the wounded innocence of a freshman seminar who, really, gets what they deserve. The answer, as anyone who has lived past forty can attest, is: almost no one, in either direction, and the sooner one makes peace with this the sooner one can get on with the business of living. But the question itself, posed for perhaps the ten-thousandth time since Michael Young coined the term in 1958 as satire and watched in horror as the world adopted it as program, deserves a moment of our attention — if only because the people asking it have lately acquired the power to rearrange institutions around their dissatisfaction.

The charge against meritocracy is that it is a fiction: that the child of the Greenwich hedge-funder who matriculates at Yale did not, in any morally coherent sense, earn her place, and that the system which pretends otherwise is a laundering operation for inherited advantage. This is true. It has always been true. It was true when Jefferson proposed raking the geniuses from the rubbish, and it was true when the Mandarins sat for their examinations in the Forbidden City. The question is not whether meritocracy is perfect — no human sorting mechanism is — but whether the proposed alternatives are anything other than older, cruder, and more nakedly hereditary forms of the same business.

Consider, by way of contrast, the small item buried in this week's cultural pages: Gustavo Dudamel and James Conlon are decamping from Los Angeles, leaving behind a concert hall designed by Frank Gehry that will outlast them both. Dudamel was plucked, as a teenager, from a state-funded music program in Venezuela called El Sistema, which took poor children and handed them violins on the radical theory that talent is distributed more democratically than opportunity. He is now among the most celebrated conductors alive. Whether he deserves his eminence in some cosmic ledger I cannot say. What I can say is that without a mechanism — call it meritocracy, call it what you like — designed to find him and lift him, he would be conducting nothing at all, and Disney Hall would be the poorer for it.

This is the part the critics tend to elide. The choice is never between meritocracy and justice; it is between imperfect sorting and no sorting, between flawed ladders and no ladders. The Guardian's columnists, secure in their bylines, are free to deconstruct the staircase by which they climbed. The rest of us, watching from below, might prefer the staircase repaired rather than dynamited.

Meanwhile, if you wish to read something genuinely worth your evening, the magazine's briefly noted reviews this week cover four books, none of which will solve the problem of desert, and all of which will reward the effort more than another thousand words on whether anyone deserves anything.

Briefly Noted Book Reviews  ·  “Sectioned,” by Meghan O’Rourke  ·  Gustavo Dudamel and James Conlon Exit L.A.
On This Day in AI History

On June 15, 2012, Facebook acquired Instagram for $1 billion, a landmark deal that demonstrated the enormous value of AI-driven image recognition and recommendation algorithms in social media's future. The acquisition proved prescient, as Instagram's visual platform would later become central to Facebook's advertising business and AI research efforts.

⬛ Daily Word — Technology
Hint: Relating to computers, the internet, and digital security threats.
Share this edition: 𝕏 Twitter/X 🔗 Copy Link ▦ RSS Feed