Vol. I  ·  No. 176 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
THURSDAY, JUNE 25, 2026 Powered by Anthropic Claude  ·  Published on Klair Trilogy International © 2026
🖶 Download PDF 🖿 Print 📰 All Editions
Today's Edition

SOFTWARE BARONS PITCH THE COMPANY THAT RUNS ITSELF

SAP, Microsoft, Google and McKinsey all sold a business on autopilot this week — but a quiet Austin conglomerate already lives there.

WALLDORF, GERMANY — SAP unveiled its "Autonomous Enterprise" at the Sapphire 2026 conference this week, pitching software agents built to run a company's books, supply chains and back office while the boss sleeps.

The German software maker says the plan folds artificial intelligence straight into its core business suite. Yesterday's data-entry grind becomes automatic action, the company claims. The pitch headlined a week when every heavyweight in the enterprise game shoved in the same chip.

Microsoft moved fast. Redmond rolled out fresh SAP-on-Azure tie-ins from the same Sapphire floor, lashing SAP's autonomous play to its own cloud.

Then McKinsey and Google Cloud announced a joint outfit — an "enterprise AI transformation group" — to carry the gospel to corporate clients. Comcast Business cut the ribbon on an Innovation Lab aimed at enterprise AI and hybrid infrastructure. Four announcements, one week, one idea: the company that minds itself.

The pitch is old wine. Software that does the work without the worker has been the dream since the punch card. The new wrinkle is the agent — AI that doesn't just suggest, it acts.

What's "autonomous" mean on the floor? An invoice that posts itself. A reorder that fires before the shelf goes bare. A ledger that closes without a clerk.

The timing tells the tale. Enterprise software has been the steadiest cash in tech — sticky contracts, fat margins, slow change. AI threatens all three, and the incumbents are racing to bolt agents on before someone eats the lunch.

Here's the rub. The giants are announcing the autonomous future. A private outfit in Austin has been quietly running one.

Trilogy International, Joe Liemandt's conglomerate, owns more than 75 enterprise software brands through ESW Capital — Aurea, IgniteTech and Skyvera among them. It buys them at one to two times annual recurring revenue, then runs them lean. Liemandt wrote that playbook in reverse years back: buy aging software cheap, strip the cost, run it on AI and remote hands.

Trilogy already plays the autonomous hand. Ephor is its AI finance platform. Totogi handles cloud billing for telcos. Klair, the in-house analytics rig, watches the portfolio's money around the clock.

The difference is the bankroll. SAP and Microsoft sell the autonomous enterprise as a product. Trilogy runs as one. Its whole cost structure rides on Crossover, the talent platform that staffs the machine from more than 130 countries at a single flat wage.

The skeptics aren't sold. Agents still hallucinate, and a self-driving ledger that books the wrong figure is worse than a slow clerk who catches it. Wall Street wants the savings; the auditors want the receipts.

So while the Sapphire stage promised a robot back office, the buyer's question stays plain. Who already wired it up — and who's still hawking the blueprint?

SAP Unveils the Autonomous Enterprise - SAP News Center  ·  Advancing enterprise AI: New SAP on Azure announcements from  ·  Comcast Business Launches Innovation Lab to Accelerate Enter

AI’s Memory Blitz Hits Apple’s Hardware Line

Apple raised prices on select MacBook and iPad models Thursday, citing surging memory and storage chip costs tied to the AI industry's datacenter buildout. The hyperscalers have been aggressively purchasing high-end components for AI servers, pressuring even Apple's typically robust supply chain. The iPhone, Apple's main revenue driver, remains unaffected, suggesting the company is protecting its core business while absorbing pressure in secondary products.

The price adjustments signal that AI demand has moved beyond cloud computing discussions into consumer devices. Chip suppliers face unprecedented demand as datacenter operators and model builders compete for capacity. Infrastructure companies like HIVE Digital are capitalizing on this trend, announcing a potential 10-year data center lease in Sweden and a $100 million notes offering.

For Apple, the challenge extends beyond managing component costs. Investors are closely monitoring the company's AI innovation and spending, as it cannot rely solely on defending hardware margins. The company must demonstrate offensive progress in AI features, silicon development and services to maintain investor confidence in its AI strategy.

AI's Funding Furnace Runs Hot: $1.5 Billion Pours Into Agents, Infrastructure, and a New Israeli Unicorn

Four deals in one week signal that enterprise AI investment has shifted from experimentation to infrastructure arms race.

NEW YORK — The AI funding cycle shows no sign of cooling. Four discrete capital events this week collectively deployed more than $1.5 billion into companies building the plumbing, agents, and compute backbone of enterprise AI — a concentration of capital that reflects both genuine commercial momentum and intensifying competitive pressure ahead of major model releases from Anthropic and OpenAI.

The largest single headline came from Tel Aviv. Decart, an Israeli AI startup, closed a $300 million round at a $4 billion valuation with Nvidia participating — a strategic signal as much as a financial one. Nvidia's direct investment in an AI model company reinforces its posture as an ecosystem builder, not merely a chip seller. Decart, which has focused on real-time generative AI and simulation, now carries a valuation that implies investors expect it to compete at the frontier tier.

Also notable: Starcloud raised $170 million in a Series A at a $1.1 billion valuation, led by Benchmark and EQT Ventures. The round, corrected and rereleased via Business Wire, positions Starcloud as a GPU cloud provider targeting AI workloads — a segment that has minted multiple unicorns in the past 18 months as demand for inference and training compute consistently outstrips hyperscaler availability.

Bret Taylor's Sierra closed nearly $1 billion in fresh capital, just months after its prior fundraise. The speed of the follow-on is unusual even by 2024 standards. Sierra builds AI-powered customer service agents and counts several Fortune 500 companies as customers. The rapid recapitalization suggests revenue traction that justifies compressing the fundraising calendar.

The agent theme extended to Anthropic, which published guidance specifically targeting financial services deployments — an acknowledgment that regulated industries require bespoke risk and compliance scaffolding before autonomous agents can be trusted with client-facing workflows.

The backdrop: both Anthropic and OpenAI are reportedly preparing mid-tier model releases in the near term, which typically accelerates enterprise procurement decisions and, consequently, venture appetite. When capability jumps are imminent, investors buy before the use-case unlock, not after. This week looked like exactly that.

Nvidia backs Israeli AI unicorn Decart in $300 million fundi  ·  Agents for financial services - Anthropic  ·  Bret Taylor's Sierra raises nearly $1 billion months after l
Haiku of the Day  ·  Claude HaikuMachines learn to think
We build empires from their dreams
Who asks them to stop?
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
Civil Liberties, Surveillance Tech, and Free Expression Are Hereby Placed Under Substantial Legal Duress
WASHINGTON, D.C.
A Convergence of Anxieties: Algorithmic Bias Emerges as the Defining Crisis of Applied AI
CAMBRIDGE, MASSACHUSETTS — It could be argued — and, it must be said, increasingly is argued, across a remarkable proliferation of peer-reviewed venues — that the question animating the present moment in artificial intelligence scholarship is no longer whether algorithmic systems encode bias, but rather by what mechanism, at what scale, and with what degree of institutional complicity such encoding occurs (a distinction that, one hastens to note, carries nontrivial implications for remediation strategy). The thesis, stated plainly: a constellation of recent research outputs converges upon the disquieting proposition that AI systems, when deployed in high-stakes domains, do not merely inherit the biases latent in their training corpora — they amplify them.
The Silicon Food Chain Learns New Hunting Grounds
WASHINGTON — In the great semiconductor forest, nothing moves alone.
We Are Living in a Simulation, and the Simulation Just Admitted It Has Bugs
AUSTIN, TEXAS — Let me tell you about the week I stopped trusting anything. It started, as most existential crises do, with physics.
AI Is Not Coming for Your Job. Your Company’s Talent Strategy Is.
AUSTIN, TEXAS — I'll be honest: the future of work discourse has become a full-contact sport where everyone is somehow both visionary and terrified.
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
Production Release

Team Tears Down Legacy, Builds Future Across Five Repos

From a surgical pipeline decommission to a live production migration, the Builder Team proved this week that the best engineering is equal parts creation and demolition.

The migration already hit prod. Let that sink in. Before most of us finished our morning coffee, @caina-barbosa's PR #1 had collapsed two separate data models — `learning_stream_items` and `sources` — into a single unified identity, wired up a recency-signal feed, baked in lazy og:image unfurling, and landed the whole thing live on Neon prod main. That's not a feature flag. That's a shipped reality. The B1 'the-feed' increment is the kind of full-stack commitment that makes product roadmaps actually move, and Caina executed it clean: data-safe, idempotent, verified on a pre-prod branch before the trigger was pulled. When the lede writes itself, you let it.

While the feed was going live, @eric-tril was quietly doing some of the most important unglamorous work in engineering: making the financial data pipeline tell the truth. The Cash Flow Statement's working-capital lines — AR, AP, deferred revenue, OCL — were carrying foreign-currency translation noise because the consolidated balance sheet collapses everything to USD. Eric's new SuiteQL-to-Redshift pipeline (PR #557 in Surtr) extracts per-subsidiary functional-currency postings alongside all three NetSuite consolidation rates, landing them in a new `month_end_balance_sheet_fx_detail` table. That table then feeds the Layer-1 drill-down in Klair (PR #3131), giving Finance an actual audit trail for the cash-flow-rate value Q versus the noisy raw delta R. And because naming conventions matter when you're operating at this precision, Eric swept both repos — Klair (#3139) and Surtr (#561) — to hard-cutover every reference from `monthly_financial_detail` to `month_end_financial_detail`. That's four PRs across two repos, one coherent data story. The breadth is the point.

@mwrshah delivered the kind of PR that only a confident team ships: pure deletion. PR #3132 rips five ECS task definitions out of the SAM template — renewals, risk-assessment, renewal-research, validation, the whole legacy `klair-udm` renewals pipeline — because Surtr's `renewals-v3` stack has fully taken over. The AWS resources were already torn down in production. This PR makes sure a future `sam deploy` can't accidentally resurrect them and run duplicate risk assessments. Stack status: `UPDATE_COMPLETE`. Legacy status: gone. That's how you close a migration.

@benji-bizzell, meanwhile, was operating across Aerie like someone who genuinely enjoys the full stack. He shipped a typed campus-coordinator override for Portfolio Personnel (PR #486), closed the admissions loop with a privacy-suppressed lead-source aggregate API for Rea (PR #489), and stripped the entire Demerits system out of the codebase (PR #487) — runtime, schema, permissions, and a Convex migration bridge to keep existing data valid through the transition. Three PRs, three different problem shapes, one engineer who clearly doesn't believe in narrow lanes.

And then there's the Google Docs add-on saga. Four consecutive PRs — #3135, #3136, #3138, #3140 — marching the add-on sidebar toward full ReviewPanel parity with the in-app experience. Section titles resolved from backend spec. Per-finding triage controls. Tool-resolution round-trips so accepted proposals don't haunt you on reload. Word-level diffs on section rewrites. All from @marcusdAIy.

"The ReviewPanel parity work is architecturally sound and the AbortController conversation is frankly beneath the level of this PR," marcusdAIy told this reporter. "Maybe write about the idempotent 204 endpoint and stop pretending race conditions are more interesting than persistence contracts. Also you misspelled my name last week."

He didn't spell his own name right when he registered his GitHub handle, but sure, I'll take the note.

@ashwanth1109 capped the day with two very different wins: AbortController guards on the AWS spend dashboard hooks (PR #3120) — no new UI, just the invisible confidence of knowing stale responses can't overwrite fresh data — and a complete rubric-v2 overhaul in Praxis-V2 (PR #6) that hardcodes a deterministic PASS/FAIL gate across 20 criteria so the LLM grades but never decides. When the verdict is computed, not inferred, the dashboard and the docx can't drift apart. That's the kind of architectural discipline that compounds.

Five repos. Seventeen PRs. One production migration already live. The Builder Team didn't just ship this week — they cleaned house, upgraded the foundation, and opened doors that weren't there yesterday.

Mac's Picks — Key PRs Today  (click to expand)
#1 — feat: ux-redesign B1 — unified source model + feed + og:image @caina-barbosa  no labels

## Summary

The full B1 "the-feed" increment: collapses learning_stream_items + sources into a single sources identity with a lifecycle status, unifies writers/readers/routes onto it, adds the recency signal + nightly feed-fill, the feed/library surface, the category rail, and lazy og:image unfurl for feed cards.

## ⚠️ Migration status — already applied to prod

migrations/0043_ux_redesign.sql was applied to Neon prod main on 2026-06-22 via the Neon migration-prep flow (temp branch tested, verified, then committed). The migration is data-safe and idempotent — verified on a prod-scale restore before applying:

- Fold all 641 learning_stream_items rows into sources (status mapped: pending→feed, bookmarked/graded→kept, discarded→discarded; provenance preserved).

- Repoint all 292 quizzes onto sources.id (ON DELETE CASCADE), zero orphan quizzes.

- Promote ~14,400 legacy DOK URLs (from facts.source + dok2_summaries.source_url) into sources rows with cross-table dedup — a URL cited in both tables becomes one row.

- Infer type for ~3,750 promoted sources from URL host (Video / Twitter / Podcast / Substack / Academic Paper).

- Residue (facts/dok2 with no extractable URL) left unlinked — no junk sources rows.

- Adds sources.status/provenance/relevance_score/og_image, brainlifts.last_activity_at/in_scope/out_of_scope/onboarding_step/onboarding_topic, drops experts.is_following + learning_stream_items table.

## What's in this branch

- Spec 01 — unified source model: lifecycle columns on sources, FK repoint, storage core (getFeed/getLibrary/updateSourceStatus/insertSource/getSourceStats).

- Spec 02 — unify writers: swarm, bookmark, chat, onboarding starter-pack all write to sources.

- Spec 03 — unify readers + retire LSI: routes/feed/library/category-counts read sources; learning_stream_items table dropped.

- Spec 04 — recency + nightly job: last_activity_at + touchBrainliftActivity + feed-fill cron.

- Specs 05/06 — feed surface + category rail.

- Spec 07 — og:image: og_image jsonb column, SSRF-guarded unfurl util, setSourceOgImage, POST /sources/:id/unfurl-og-image, useOgImage hook wired into feed cards.

- B1 data migration: the data-safe fold in 0043_ux_redesign.sql (replaces the original naive "no backfill" block that couldn't apply to a populated DB).

- server/scripts/enrich-source-titles.ts: one-off Exa-backed title enrichment for bare-host-title promoted sources (~3,940 candidates, ~\$3, runs separately after deploy).

## Verification

- npm run build green.

- Migration dry-run on prod-restore copy: 15,166 sources, 0 orphan quizzes, 11,731 shared URLs each deduped to one row, residue left NULL, cascade/set-null FK semantics confirmed by live delete test.

- Same checks re-verified on the Neon temp branch before commit: 15,166 sources, 0 orphans, 0 junk titles, 100% URL-bearing facts/dok2 linked.

## Post-deploy (optional, separate step)

- Run tsx server/scripts/enrich-source-titles.ts against prod to replace bare-host titles (~3,940 rows) with real article titles via Exa. Idempotent, ~\$3.

#6 — Rubric v2: binary CEO + finance gates, deterministic verdict, dossier surfaces @ashwanth1109  no labels

## Summary

Reworks the review pipeline onto the 20-criterion rubric-v2 — 13 CEO gates (5 Core Values + 8 Alpha Moves) plus 7 finance auto-fails (14–20) — with a deterministic compute gate as the single source of truth for the verdict. The LLM grades individual criteria and writes prose; it never decides the overall PASS/FAIL. Dashboard and docx both decompose the *computed* verdict, so they can't drift from the gate.

This PR delivers all four waves of the epic in one branch.

### Wave 0 — foundations (#2, WS1–WS3)

- 20-criterion rubric content + idempotent seed (db/seed-rubric-v2.ts, rubric-v2-content.ts).

- New columns: gate_kind, bar, and citation / value_observed / threshold (all nullable so existing rows survive the push).

- Typed artefact roles threaded into PlanDocument (documents.ts).

- Financial-model cell-grid serializer (model-grid.ts) yielding address-keyed Sheet!Addr cells.

### Wave 1 — enforcement (#3, WS4/WS6)

- Per-criterion evaluator with a mandatory-citation + banned-language contract and numeric-bar injection (evaluate-criterion.ts).

- Deterministic validate + compute gate (validate-compute.ts): rejects uncited PASS / unfixed FAIL / hedge language, computes PASS/FAIL and the skill's counts. Synthesis reads the computed grade instead of re-deriving it.

### Wave 2 — finance gates & reconciliation (#4, WS5)

- reconcile-metrics.ts emits the metric × artefact reconciliation table (tuition, enrolment, gross retention, steady-state margin, IRR) with material-disagreement flags.

- evaluate-finance-gate.ts grades 14–20 against the cell grid + reconciliation table with exact-cell citations; criterion 20 keys off the disagreement flags.

- run.ts loads the grid, reconciles, and fans out split by gate kind (evaluate-criterion-* vs evaluate-finance-gate-*) with a branched bounded re-ask. Every step returns a JSON-serializable trace payload carrying token usage.

### Wave 3 — deliverables (#5, WS7)

- criterion-grouping.ts shares the CEO/finance split across surfaces.

- Dashboard: Observed/Bar chips + citation per criterion; sectioned breakdown (Core Values / Alpha Moves / Finance auto-fail); header stats (passed/13, critical fails, finance fails/7); a DO-NOT-APPROVE · RESUBMIT banner with top-3 drivers when a finance gate fires.

- Word memo (docx-export.ts): verdict-first (Verdict → Core Values → Alpha Moves → finance auto-fail table), each entry with evidence + citation, fixes on FAIL.

## Verification

- tsc --noEmit (full project) → clean.

- eslint on all changed/new files → clean.

- Not yet run live. A real trace requires the schema push + seeded rubric + a plan with an uploaded financial model — none runnable from the dev environment. The Inngest run trace (reconcile-metrics, evaluate-finance-gate-*) is where to confirm cell-level citations and the criterion-20 disagreement on a real plan.

## Required follow-up (operational, maintainer-run)

1. pnpm db:push — creates the gate_kind/bar + citation columns and the criterion_gate_kind enum / finance category value.

2. pnpm db:seed:rubric — seeds + activates the v2 rubric (the finance fan-out reads each criterion's gate_kind).

3. Upload a financial-model document so model_sheet cells exist — required for finance gates to produce cell citations.

Closes #2 · Closes #3 · Closes #4 · Closes #5 · Part of #1

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

#557 — feat(netsuite-balance-sheet-fx-detail): SuiteQL→Redshift FX-detail pipeline for cash-flow CTA (KLAIR-2806) @eric-tril  approved

## What

New daily pipeline netsuite-balance-sheet-fx-detail that extracts, per (accounting period, subsidiary, account), the functional-currency net postings plus the three NetSuite consolidation rates (current / average / historical), landing them in Redshift staging_netsuite.month_end_balance_sheet_fx_detail.

## Why

The Klair Cash Flow Statement's working-capital lines (AR, AP & accrued, deferred revenue, OCL, …) need their true cash movement separated from foreign-currency translation (CTA) noise. The consolidated staging_netsuite.balance_sheet is USD-only — it collapses away the per-subsidiary local balances and exchange rates the decomposition requires. This pipeline preserves them. It is purely additive: it retires nothing, and balance_sheet remains the reconciliation target.

The pipeline lands raw facts only; all FX arithmetic (running balance, CTA decomposition, reconciliation) happens downstream in klair-api.

## How

- Cloned from netsuite-monthly-financial-detail; netsuite_auth.py / netsuite_client.py / loader scaffolding copied verbatim.

- Flat, constraint-compliant SuiteQL: no derived tables, no subqueries in JOIN ON, no BUILTIN.DF in a GROUP BY SELECT, the Group consolidation id injected as an integer literal, always period-bounded (avoids the full-history scan that times out).

- Idempotent per-period DELETE+COPY via the Redshift Data API; daily rolling (current + 3 prior) with backfill built in.

## Validation

- Extract reconciles to staging_netsuite.balance_sheet: the cash component (Q) reproduces Finance's CTA Balance Audit to ~0.2%, and the balance anchors (start/end/Total CTA) tie to the cent.

- Inception backfill (Jul 2015 → Jun 2026, 170,099 rows) loaded and verified in Redshift.

- 102 unit tests; ruff clean; pipeline.json passes the pipeline-cdk schema + AWS-cap config tests.

## Deploy note

Ships with schedule.enabled=false — flip to true after the first post-deploy manual invoke + 14100 reconciliation succeeds (documented in the README).

## Out of scope (separate, Finance-gated follow-up)

- The unrealised-gains adjustment (a balance-based FX revaluation; historical_rate is captured here so the data is ready, but the formula awaits Finance confirmation).

- A dormant-subsidiary rate extract — needed only for balance-based FX (not for the cash-flow line, which anchors to balance_sheet).

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

#3131 — feat(mfr): cash-flow-rate (Q) + CTA decomposition for CF working-capital Layer-1 (KLAIR-2806) @eric-tril  approved

## What & why

Finance's Cash Flow working-capital lines (AR, Prepaid, AP & accrued, Deferred revenue, OCL) need the cash-flow-rate value (Q) — the real cash component — rather than the raw consolidated balance_sheet QTD delta (R), which carries FX-translation noise. This wires up the new staging_netsuite.month_end_balance_sheet_fx_detail table (per-subsidiary functional postings + NetSuite consolidation rates) to compute Q and surface it, with a full per-account + per-subsidiary audit trail, at the Layer-1 / drill-down level.

The CF Statement line itself is unchangedNON_DERIVABLE_ITEMS is untouched; the displayed Statement value still comes from manual entry. This only changes the auto-derived Layer-1 value and its drill-down.

## Method (Finance's CTA Analysis, per account)

- R (Total CTA) = balance_sheet QTD delta — the exact anchor

- Q (Net Posting at Cash Flow Rate) = Σ net_postings × cash_flow_rate

- P (Net Posting Rate Difference) = Σ net_postings × (general − cash_flow)

- O (Beginning Balance Rate Difference) = R − P − Q (residual)

Q is the displayed value, except when its residual O is implausibly large vs Q (|O| > 0.20·|Q| and |O| > $200k — elimination / low-FX accounts like 21000): there it falls back to R (the exact anchor) and is flagged source_kind="fx_anchor". The same guard runs in the Layer-1 headline so it ties to the drill-down total.

## Validation (vs Finance's April 2026 sheet)

- 5/7 AR accounts tie to the cent (14600, 14000-AR Import, 14300, 14200, 21202); R anchors every account.

- 14100 within ~0.2–0.3% (rate precision, per the pipeline author's stated tolerance).

- 21000 displays the exact anchor R (its Q is unreliable due to an ESW Capital (Elimination) subsidiary — a Surtr-extract follow-up).

## Changes

Backend

- mfr_shared_queries: fetch_fx_cf_rate_by_account (q_raw + p_raw), fetch_fx_subsidiary_qtd

- cash_flow_service: _aggregate_fx_components, _apply_fx_components, _fx_q_unreliable guard, _fetch_bs_delta_detail(enrich_fx=True), _compute_layer_1_values → Σ Q with the R-fallback guard

- router: CashFlowFxSubsidiaryRow + new CashFlowDetailRow fields (total_cta, net_posting_rate_diff, beginning_bal_rate_diff, source_kind, subsidiaries)

Frontend

- CashFlowDetailPanel BSDeltaDetail: O/P/Q/R decomposition columns, expandable per-subsidiary audit breakdown, Finance terminology, (R anchor) / (IS-sourced) / (no FX) chips, enriched CSV export

## Tests

- Backend TestFxComponents (Q/P, residual O, signs, subsidiaries, anchor guard, Layer-1) — 281 pass in tests/mfr/financial_statements/

- Frontend FX-decomposition panel tests — 20 pass

- ruff / pyright / tsc / eslint / vite build clean

## Out of scope / follow-ups

- 21000 elimination scope & 14100 rate precision are Surtr-extract items (the audit trail surfaces them).

- The separate "Loan fee amortisation" AP Layer-1 adjustment is not in any ingested Redshift table (likely deferred fees on the new March term loans) — needs a NetSuite account number from Finance.

- Unrealised-gains adjustment + Statement-line auto-derivation remain deferred (Finance-blocked).

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

http://localhost:3001/monthly-financial-reporting

https://github.com/user-attachments/assets/b4cde9b4-7591-4dd0-86df-9ddbd1cf3f7a

#3132 — 351-risk-assessment-cutover @mwrshah  no labels

Last-mile changes to decommission the legacy klair-udm renewals data pipeline, now that generation and risk assessment run on Surtr (renewals-v3 + renewals-risk-assessment).

The live AWS resources were already torn down; this removes them from the SAM template so a future sam deploy can't recreate them and reintroduce duplicate risk-assessment runs. The matching deploy has already been applied to the klair-udm stack (drift cleared, stack UPDATE_COMPLETE).

## Removed from klair-udm/template.yaml

- 5 ECS task definitions: renewals, risk-assessment, renewal-research, validation, retention-validation

- 4 EventBridge rules: DailyRenewalsPipelineRule, RiskAssessmentTriggerRule, ValidationTriggerRule, MonthlyRetentionValidationRule

- 5 CloudWatch log groups for the above

- 7 stack outputs for the above

- The renewals/risk-assessment/validation/retention image lifecycle rules on the shared ECR repo

## Pruned dangling references

- Removed the deleted log-group !GetAtts from the shared EcsTaskExecutionRole

- Removed the deleted task-def !Refs from RenewalsPipelineEventRole

## Deleted source

- klair-udm/retention-validation-pipeline/ (its task def, schedule, and log group are gone)

## Kept (shared / unrelated, still in use)

- EcsTaskExecutionRole, EcsTaskRole, RenewalsPipelineEventRole

- The co-jira and notion-rca-hub-sync pipelines (task defs, log groups, schedule rules)

- The renewals product backend (klair-renewals, renewals_backend-*) — out of scope

The Builder Desk  —  Engineer Spotlight
🏆 Engineer Spotlight

SEVENTEEN PRs IN TWENTY-FOUR HOURS: THE BUILDER TEAM DOES NOT SLEEP, DOES NOT REST, DOES NOT STOP

Five repos, six engineers, and one Marcus who appears to have merged a PR every time someone blinked.

Seventeen pull requests. Five repositories. Twenty-four hours on the clock. The Builder Team did not merely show up yesterday — they showed up, clocked in, and then apparently forgot to clock out. Klair led the charge with nine — NINE — active PRs, followed by Aerie and Surtr each logging three, with Praxis-V2 and Brainlift-Platform each registering a single, dignified contribution that still counts, because here at the Numbers Desk every PR is a victory lap.

@eric-tril and @marcusdAIy are tied at five PRs apiece and the rivalry is as beautiful as it is completely invented by this correspondent. Eric spent his day in the unglamorous trenches of data schema correctness, renaming `monthly_financial_detail` to `month_end_financial_detail` across no fewer than three repos — Klair (#3139), Surtr (#561), and Surtr again (#560) — with the calm, methodical energy of a man who has decided that consistency is a moral virtue. Marcus, meanwhile, was running a one-man P5.9 feature sprint through Klair, dropping #3135, #3136, #3137, #3138, and #3140 in what can only be described as a sequential assault on the add-on findings pipeline. Section titles, word-diff previews, tool-resolution persistence, sidebar FE — the man did not skip a step. @benji-bizzell shipped three in Aerie alone — #486, #487, and #489 — adding a campus coordinator override, nuking the entire Demerits system (goodbye, Demerits, we will not miss you), and standing up a lead-source aggregate API. @mwrshah and @caina-barbosa each posted one PR to the board, and one PR on the Builder Team is one more than zero, which is the only math that matters.

And then there is @ashwanth1109. Two PRs. Two. The man who ships like a tidal event, reduced — temporarily, cosmically — to a pair. But what a pair. PR #3120 in Klair dropped stale-response guards via AbortController onto the `use*ByBU` hooks in what Ashwanth himself reportedly described as "a fix so obvious I'm embarrassed it needed a ticket." Then there's PR #6 in Praxis-V2 — number SIX, a repo so new the PRs are still in single digits — where he landed binary CEO and finance gates, a deterministic verdict engine, and dossier surface logic for Rubric v2. When asked to explain the architecture to this correspondent, Ashwanth allegedly replied: "I don't explain diffs, Brick. I write them." His dismissal was immediate and total.

The Overflow Desk today is practically a second full edition. Mac left twelve PRs on the cutting room floor, and each one deserved a headline. The Demerits removal in Aerie (#487) is the kind of quiet institutional surgery that makes a codebase 3% less haunted — Benji executed it without ceremony, which is exactly how you remove a system called Demerits. Eric's rename cascade across Klair and Surtr (#3139, #561, #560) is a masterclass in cross-repo discipline: one naming decision, three repos, zero loose ends. And Marcus's P5.9 chain — five consecutive PRs building the add-on findings UX from proposal payload to ReviewPanel parity — reads less like a pull request queue and more like a chapter breakdown for a novel about software doing exactly what it was designed to do.

Morale is, as always, at an all-time high. The Numbers Desk has confirmed this independently.

Brick's Overflow — PRs Mac Didn't Cover  (click to expand)
#6 — Rubric v2: binary CEO + finance gates, deterministic verdict, dossier surfaces @ashwanth1109  no labels

## Summary

Reworks the review pipeline onto the 20-criterion rubric-v2 — 13 CEO gates (5 Core Values + 8 Alpha Moves) plus 7 finance auto-fails (14–20) — with a deterministic compute gate as the single source of truth for the verdict. The LLM grades individual criteria and writes prose; it never decides the overall PASS/FAIL. Dashboard and docx both decompose the *computed* verdict, so they can't drift from the gate.

This PR delivers all four waves of the epic in one branch.

### Wave 0 — foundations (#2, WS1–WS3)

- 20-criterion rubric content + idempotent seed (db/seed-rubric-v2.ts, rubric-v2-content.ts).

- New columns: gate_kind, bar, and citation / value_observed / threshold (all nullable so existing rows survive the push).

- Typed artefact roles threaded into PlanDocument (documents.ts).

- Financial-model cell-grid serializer (model-grid.ts) yielding address-keyed Sheet!Addr cells.

### Wave 1 — enforcement (#3, WS4/WS6)

- Per-criterion evaluator with a mandatory-citation + banned-language contract and numeric-bar injection (evaluate-criterion.ts).

- Deterministic validate + compute gate (validate-compute.ts): rejects uncited PASS / unfixed FAIL / hedge language, computes PASS/FAIL and the skill's counts. Synthesis reads the computed grade instead of re-deriving it.

### Wave 2 — finance gates & reconciliation (#4, WS5)

- reconcile-metrics.ts emits the metric × artefact reconciliation table (tuition, enrolment, gross retention, steady-state margin, IRR) with material-disagreement flags.

- evaluate-finance-gate.ts grades 14–20 against the cell grid + reconciliation table with exact-cell citations; criterion 20 keys off the disagreement flags.

- run.ts loads the grid, reconciles, and fans out split by gate kind (evaluate-criterion-* vs evaluate-finance-gate-*) with a branched bounded re-ask. Every step returns a JSON-serializable trace payload carrying token usage.

### Wave 3 — deliverables (#5, WS7)

- criterion-grouping.ts shares the CEO/finance split across surfaces.

- Dashboard: Observed/Bar chips + citation per criterion; sectioned breakdown (Core Values / Alpha Moves / Finance auto-fail); header stats (passed/13, critical fails, finance fails/7); a DO-NOT-APPROVE · RESUBMIT banner with top-3 drivers when a finance gate fires.

- Word memo (docx-export.ts): verdict-first (Verdict → Core Values → Alpha Moves → finance auto-fail table), each entry with evidence + citation, fixes on FAIL.

## Verification

- tsc --noEmit (full project) → clean.

- eslint on all changed/new files → clean.

- Not yet run live. A real trace requires the schema push + seeded rubric + a plan with an uploaded financial model — none runnable from the dev environment. The Inngest run trace (reconcile-metrics, evaluate-finance-gate-*) is where to confirm cell-level citations and the criterion-20 disagreement on a real plan.

## Required follow-up (operational, maintainer-run)

1. pnpm db:push — creates the gate_kind/bar + citation columns and the criterion_gate_kind enum / finance category value.

2. pnpm db:seed:rubric — seeds + activates the v2 rubric (the finance fan-out reads each criterion's gate_kind).

3. Upload a financial-model document so model_sheet cells exist — required for finance gates to produce cell citations.

Closes #2 · Closes #3 · Closes #4 · Closes #5 · Part of #1

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

#487 — feat(operations): remove Demerits system @benji-bizzell  no labels

## Summary

- Remove the Demerits admin surface, Convex runtime module, shared source contract, and active permission/capability wiring.

- Add a temporary Convex migration bridge that keeps existing dev/prod data schema-valid while removing stale role refs and legacy rows.

- Update generated API/tests/docs so active code no longer exposes Demerits behavior.

## Why

The Demerits feature is unused, and existing deployment data only contains stale/test records. Removing the runtime directly exposed a Convex schema validation conflict in persisted role docs, so this PR ships the safe widen/migrate bridge first.

## Business Value

Simplifies Aerie operations/admin UX and removes dead permission/API surface area while preserving a safe path through Convex schema validation for dev and prod.

## Breaking changes

Removes the Demerits feature surface and @bran/contracts/demerit-sources export. The temporary permissions.canLogDemerits field and demerits table remain schema-compatible until the post-deploy prod migration verifies clean; a follow-up PR should narrow the schema.

## Test plan

- [x] pnpm -C chat exec vitest run convex/removeDemeritsMigration.test.ts convex/users/roles.test.ts convex/users/identity.test.ts

- [x] pnpm -C packages/contracts test -- src/capabilities.test.ts

- [x] pnpm typecheck

- [x] pnpm lint

- [x] Dev dry-run on fleet-goat-601 found 3 stale role docs and 2 legacy Demerits rows

- [x] Dev migration executed: patched 3 roles, deleted 2 rows

- [x] Dev verification dry-run returned rowsWithChanges: 0

- [ ] After this PR deploys to prod, run migrations/removeDemerits:run dry-run/execute/verify with --prod

- [ ] Follow-up PR: remove the temporary schema bridge after prod verifies clean

#560 — chore(netsuite-balance-sheet-fx-detail): align pipeline_name with month_end naming @eric-tril  approved

## What

Renames the display pipeline_name for the netsuite-balance-sheet-fx-detail pipeline:

"NetSuite Balance Sheet FX Detail Pipeline""NetSuite Month End Balance Sheet FX Detail Pipeline"

## Why

Aligns the human-readable name with the Redshift table it produces (staging_netsuite.month_end_balance_sheet_fx_detail) and the MFR month_end_ table-naming convention. This change was made during PR #557 review but never committed, so it didn't land with the merge.

## Scope

Display string only — no behavior change. pipeline_id, handler, runtime, schedule, environment, and iam_statements are all unchanged, so deployed resource names (derived from pipeline_id) are unaffected.

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

#3120 — KLAIR-2858 fix(aws-spend): add stale-response guards (AbortController) to use*ByBU hooks @ashwanth1109  approved

## Demo

### Check to ensure AWS spend dashboard loads as expected

<img width="2624" height="1636" alt="image" src="https://github.com/user-attachments/assets/9696a633-2ca3-4864-8376-968c0d2b933e" />

_No new user-visible behavior — this is an internal race-condition guard (inline AbortController per fetch-in-effect hook). The proof is the stale-response regression tests: their assertions show a superseded in-flight response cannot overwrite the fresh data for the newly-selected input._

Logic / test proof — stale-response guard

Ran the 6 changed AWSSpend hook specs (the 5 high-value *ByBU specs + useAccountMappings) directly:

$ pnpm vitest run \

src/screens/AWSSpend/hooks/useAWSSpendByBU.spec.ts \

src/screens/AWSSpend/hooks/useNetAmortizedByBU.spec.ts \

src/screens/AWSSpend/hooks/useNetAmortizedBvaByBU.spec.ts \

src/screens/AWSSpend/hooks/useWoWHeatmapByBU.spec.ts \

src/screens/AWSSpend/hooks/useNetAmortizedWoWHeatmapByBU.spec.ts \

src/screens/AWSSpend/hooks/useAccountMappings.spec.ts

✓ src/screens/AWSSpend/hooks/useWoWHeatmapByBU.spec.ts (5 tests)

✓ src/screens/AWSSpend/hooks/useAWSSpendByBU.spec.ts (5 tests)

✓ src/screens/AWSSpend/hooks/useNetAmortizedBvaByBU.spec.ts (5 tests)

✓ src/screens/AWSSpend/hooks/useNetAmortizedWoWHeatmapByBU.spec.ts (5 tests)

✓ src/screens/AWSSpend/hooks/useNetAmortizedByBU.spec.ts (7 tests)

✓ src/screens/AWSSpend/hooks/useAccountMappings.spec.ts (8 tests)

Test Files 6 passed (6)

Tests 35 passed (35)

(The [useAWSSpendByBU] Fetch error: Error: Network error lines in stderr are the expected console.error output from the deliberate "rejected fetch" test cases — not failures; every file shows a green checkmark.)

The load-bearing assertion is the regression test present in each *ByBU spec — _"ignores a stale in-flight response so it cannot overwrite fresh data"_. It fires the first request as an unresolved promise, rerenders with a changed quarter to supersede it (which aborts the first request and fires a fresh one), lets the fresh request resolve, then resolves the stale promise last (out-of-order) and asserts the fresh data survived:

// The stale (first) request now resolves LAST with different data.

resolveStale(MOCK_DATA);

await stalePromise;

// It was aborted, so it must not have overwritten the fresh result.

expect(result.current.data).toHaveLength(1);

expect(result.current.data[0].bu).toBe('Finance');

This is the exact bug class fixed for useCostMovementByBU in KLAIR-2857: without the if (signal?.aborted) return; gate before setData, the late-resolving stale response would clobber the fresh data. With the guard, it is dropped.

Most at risk from this change: the happy-path data flow of each guarded hook (gating / transform / loading / error semantics must be unchanged) and the out-of-order stale-resolution path itself. Both are exercised by the specs above — each *ByBU spec covers happy-path + null-input early-return + disabled + rejected-fetch + the stale-response regression, and useAccountMappings retains its existing "drops a stale fetch result when the quarter changes" test (its monotonic requestIdRef flag guard was kept and signal threading added on top for true request cancellation). All 35 assertions held.

> _Note (autonomous mode): the proof above is a read-only local Vitest run; no mutation, network, or production resource was touched._

---

## Feature: AWS Spend Dashboard — stale-response guards for fetch-in-effect hooks

Linear: [KLAIR-2858 — AWS Spend: add stale-response guards (AbortController) to use*ByBU hooks](https://linear.app/builder-team/issue/KLAIR-2858/aws-spend-add-stale-response-guards-abortcontroller-to-usebybu-hooks)

### Feature overview

The AWS Spend dashboard's data hooks each run a fetchData callback inside a useEffect keyed on their inputs (quarter, includeBedrock, BU/class selection, account/jiraKey, etc.). When a user rapidly flips an input, the effect re-fires before the previous request has resolved. Without a guard, a slow earlier response can resolve *after* the newer one and overwrite the fresh data with stale data — the screenshot-class bug already fixed for useCostMovementByBU in KLAIR-2857.

This PR retrofits the proven useCostMovementByBU guard idiom onto the remaining unguarded fetch-in-effect hooks under klair-client/src/screens/AWSSpend/hooks/. The idiom is a true inline AbortController per hook: fetchData accepts an optional signal?: AbortSignal, forwards it to the awsSpendApi / coTicketsApi call, returns early before any state setter if signal?.aborted, swallows AbortError in the catch, skips setLoading(false) when aborted, and the effect creates a fresh AbortController, calls fetchData(controller.signal), and returns () => controller.abort() so an in-flight request is cancelled when inputs change or the component unmounts.

### Spec

[46-use-by-bu-stale-response-guards](features/aws-spend/aws-spend-dashboard/specs/46-use-by-bu-stale-response-guards/spec.md) — Apply the useCostMovementByBU inline-AbortController stale-response guard to all remaining unguarded fetch-in-effect hooks under src/screens/AWSSpend/hooks/, with rapid-input-switch regression tests for the high-value *ByBU hooks.

### Implementation summary

21 hooks guarded with the inline AbortController idiom mirroring useCostMovementByBU:

- 5 primary *ByBU hooks (highest-race surfaces — inputs flip via UI selection):

- useNetAmortizedByBU.ts, useAWSSpendByBU.ts, useNetAmortizedBvaByBU.ts, useWoWHeatmapByBU.ts, useNetAmortizedWoWHeatmapByBU.ts

- 13 other high-value hooks (summary / trends / CO-tickets / adjustments / RCA / drill-down daily / classes / unmapped):

- useAWSSpendSummary.ts, useNetAmortizedSummary.ts, useAWSSpendTrends.ts, useNetAmortizedTrends.ts, useAWSClasses.ts, useUnmappedAWSSpend.ts, useAWSSpendAdjustments.ts, useNetAmortizedAdjustments.ts, useRCAEvents.ts, useCOTickets.ts, useCOTicketDailySpend.ts, useCOAccountDailySpend.ts, useAWSSpendDailyCosts.ts

- 3 low-race hooks (stable deps, rarely race — guarded for consistency + unmount cancellation):

- useAWSBUList.ts, useCOSummary.ts, and useAccountMappings.ts — the latter kept its existing monotonic requestIdRef flag guard and additionally threads signal through fetchMappedAccounts / fetchUnmappedAccountMappings for true request cancellation (it previously dropped stale writes via the flag but never aborted the in-flight network request).

No API-service changes. awsSpendApi.ts and coTicketsApi.ts already accept and forward signal?: AbortSignal as the trailing arg on every fetch function the listed hooks call — this is a hook-layer-only change.

Existing gating (null-quarter early returns, enabled flags, quarterA >= quarterB-style ordering guards, the selectedBUsKey memo in the WoW hooks) is preserved verbatim; only the awaited call is wrapped.

pnpm tsc --noEmit clean; pnpm lint:pr clean.

### Test coverage

6 spec files (4 created, 2 extended):

- CreateduseAWSSpendByBU.spec.ts, useNetAmortizedBvaByBU.spec.ts, useWoWHeatmapByBU.spec.ts, useNetAmortizedWoWHeatmapByBU.spec.ts. Each covers: happy-path + null-input early-return + disabled + rejected + a genuine stale-response regression (fire the first request as an unresolved promise, rerender with a changed input to supersede it, let the fresh request resolve, then resolve the stale promise last and assert the fresh data survived).

- ExtendeduseNetAmortizedByBU.spec.ts (added the stale-response test + expect.any(AbortSignal) call-shape assertions) and useAccountMappings.spec.ts (existing "drops a stale fetch result when the quarter changes" test retained; call-shape updated for the trailing signal).

Full AWSSpend Vitest suite green: 83 files / 973 tests.

### Self-review

Self-review: no issues found — guard placement, cleanup-abort, preserved gating, signal threading, useAccountMappings flag-kept-plus-signal, and test genuineness all verified.

#3138 — P5.9f: add-on tool-resolution persistence round-trip + FE consumption (KLAIR-2926) @marcusdAIy  approved

<!-- CURSOR_AGENT_PR_BODY_BEGIN -->

Backend slice of P5.9f. FE work (accept mutex + batch "address all in section") follows in a separate PR.

Linear: KLAIR-2926 (sub-issue of KLAIR-2906; feeds the rich sidebar KLAIR-2913).

## Summary

Give the Google Docs add-on a tool-resolution round-trip so a proposal the user Accepted/Rejected in the sidebar does not reappear as pending on reload — mirroring the in-app proposalAcceptQueue / markToolCallResolved persistence.

Two backend halves:

1. Write: new POST /board-doc/addon/tool-resolutions (204, idempotent).

2. Rehydrate: additive resolved_tool_use_ids on AddonChatResponse + AddonReviewResponse so the sidebar can filter already-resolved proposals on every read.

## Why It's Needed

Sidebar proposals reappear as pending on reload because the add-on has no resolution persistence — a parity gap vs. the in-app proposalAcceptQueue. Once the sidebar user clicks Accept/Reject on a ChatToolProposal, that tool_use_id needs to land on the session's resolved_tool_use_ids set (the same set the in-app wizard chat hydrates from), and the next add-on read needs to surface that set so the FE can hide the card. Without this round-trip every sidebar reload re-surfaces every prior proposal as actionable, identical to the bug B3.14 fixed on the in-app surface.

## Changes

- New endpoint POST /board-doc/addon/tool-resolutions in klair-api/routers/board_doc_router.py:

- Body-carried {google_doc_id, tool_use_id} for consistency with the rest of the /addon/* POST family (review-run / propose / chat / finding-status).

- google_doc_id validated via AddonGoogleDocIdField; tool_use_id min_length=1, max_length=100 (mirrors the in-app wizard_resolve_tool_call Path-param constraint so a buggy caller can't bloat the resolved set).

- Auth: Depends(get_user_from_google_oidc) + _assert_addon_session_access via the shared _resolve_addon_session helper — same gate as every other /addon/* route.

- Persists via the existing WizardSession.mark_tool_call_resolved model method, run through _save_with_merge_retry_or_raise so the 409 (retry exhausted) / 500 (storage failure) / 404 (deleted mid-flight) mapping is one source of truth with the in-app endpoint.

- Idempotent by design: a second POST with the same tool_use_id is a 204 no-op (the model method dedups), matching the in-app contract the FE relies on for double-clicks on Accept/Reject.

- Rehydration field resolved_tool_use_ids: list[str] = Field(default_factory=list) added to:

- AddonChatResponse — populated after the chat turn persists, so the FE can filter against the just-returned proposals.

- AddonReviewResponse — populated in both the reviewed branch (via _addon_review_from_results) and the needs_review branch (so a sidebar reload after Accept/Reject doesn't re-surface stale proposals from a chat that ran before any review).

- Updated one strict-equality assertion in test_addon_chat.py for the new additive field.

### Contract surface affected

- _addon_review_from_results(google_doc_id, user_email, review, resolved_tool_use_ids=None) — added an optional kwarg (default None → empty list). All existing call sites pass it explicitly; nothing outside this router consumes the helper.

## Breaking Changes

None — additive new add-on endpoint + additive optional response fields. The in-app POST /wizard/{id}/tool-resolutions/{tool_use_id} and every existing /addon/* route are unchanged. The new fields default to [] so existing JSON consumers that don't read them are unaffected.

## Test Plan

uv run pytest tests/board_doc/test_addon_tool_resolutions.py -v

# -> 18 passed in 1.55s

New test counts (18 total):

- 204 happy path — persisted, distinct ids accumulate (3 tests).

- Idempotent — repeat POST with same id is 204, no duplicate appended (1).

- Access control parity with sibling /addon/* routes — outsider 403; owner / BU-scoped / Klair superuser 204 (3).

- 404 — unknown google_doc_id (1).

- 422 — malformed google_doc_id, empty tool_use_id, oversized tool_use_id (>100 chars) (3).

- 409ConcurrentModificationError from save_with_merge_retry (detail does not leak underlying text) (1).

- 500StorageError from save_with_merge_retry, and StorageError from the doc-lookup path (no detail leak on either) (2).

- Rehydrationresolved_tool_use_ids surfaced on AddonReviewResponse (reviewed and needs_review states), on AddonChatResponse (populated + empty-default), and end-to-end POST→GET round-trip (4).

Regression sweep across related modules — all green:

uv run pytest tests/board_doc/test_addon_chat.py \

tests/board_doc/test_addon_finding_status.py \

tests/board_doc/test_addon_propose.py \

tests/board_doc/test_addon_read_slice.py \

tests/board_doc/test_addon_review_run.py \

tests/board_doc/test_tool_call_resolution_b3_14.py

# -> 102 passed in 4.24s

(5 pre-existing failures in the broader tests/board_doc/ suite — test_temperature_unsupported_guard.py, test_access_control_coverage.py::test_every_active_bu_has_an_owner — also fail on main and are unrelated to this change.)

Lint / type-check (per CLAUDE.md):

uv run ruff format routers/board_doc_router.py tests/board_doc/test_addon_tool_resolutions.py tests/board_doc/test_addon_chat.py

uv run ruff check routers/board_doc_router.py tests/board_doc/test_addon_tool_resolutions.py tests/board_doc/test_addon_chat.py

uv run pyright routers/board_doc_router.py

# -> all clean (0 errors, 0 warnings)

## Verification Artifact

$ uv run pytest tests/board_doc/test_addon_tool_resolutions.py -v

============================= test session starts ==============================

collected 18 items

tests/board_doc/test_addon_tool_resolutions.py::test_addon_resolve_tool_call_persists_and_returns_204 PASSED [ 5%]

tests/board_doc/test_addon_tool_resolutions.py::test_addon_resolve_tool_call_idempotent_on_repeat PASSED [ 11%]

tests/board_doc/test_addon_tool_resolutions.py::test_addon_resolve_tool_call_distinct_ids_accumulate PASSED [ 16%]

tests/board_doc/test_addon_tool_resolutions.py::test_addon_resolve_tool_call_403_for_outsider PASSED [ 22%]

tests/board_doc/test_addon_tool_resolutions.py::test_addon_resolve_tool_call_204_for_bu_scoped_non_owner PASSED [ 27%]

tests/board_doc/test_addon_tool_resolutions.py::test_addon_resolve_tool_call_204_for_klair_superuser PASSED [ 33%]

tests/board_doc/test_addon_tool_resolutions.py::test_addon_resolve_tool_call_404_when_no_session_for_doc PASSED [ 38%]

tests/board_doc/test_addon_tool_resolutions.py::test_addon_resolve_tool_call_422_on_malformed_doc_id PASSED [ 44%]

tests/board_doc/test_addon_tool_resolutions.py::test_addon_resolve_tool_call_422_on_empty_tool_use_id PASSED [ 50%]

tests/board_doc/test_addon_tool_resolutions.py::test_addon_resolve_tool_call_422_on_oversized_tool_use_id PASSED [ 55%]

tests/board_doc/test_addon_tool_resolutions.py::test_addon_resolve_tool_call_409_on_concurrent_modification PASSED [ 61%]

tests/board_doc/test_addon_tool_resolutions.py::test_addon_resolve_tool_call_500_on_storage_error_without_leaking_detail PASSED [ 66%]

tests/board_doc/test_addon_tool_resolutions.py::test_addon_resolve_tool_call_500_when_lookup_storage_fails PASSED [ 72%]

tests/board_doc/test_addon_tool_resolutions.py::test_addon_review_response_includes_resolved_tool_use_ids PASSED [ 77%]

tests/board_doc/test_addon_tool_resolutions.py::test_addon_review_response_includes_resolved_ids_in_needs_review_state PASSED [ 83%]

tests/board_doc/test_addon_tool_resolutions.py::test_addon_chat_response_includes_resolved_tool_use_ids PASSED [ 88%]

tests/board_doc/test_addon_tool_resolutions.py::test_addon_chat_response_resolved_ids_default_empty_for_fresh_session PASSED [ 94%]

tests/board_doc/test_addon_tool_resolutions.py::test_addon_review_response_resolved_ids_round_trip_with_endpoint PASSED [100%]

======================== 18 passed, 1 warning in 1.55s =========================

<!-- CURSOR_AGENT_PR_BODY_END -->

<div><a href="https://cursor.com/agents/bc-e94f39a0-7161-4aba-9eeb-14d3e5b11164"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-web-light.png"><img alt="Open in Web" width="114" height="28" src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a href="https://cursor.com/background-agent?bcId=bc-e94f39a0-7161-4aba-9eeb-14d3e5b11164"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img alt="Open in Cursor" width="131" height="28" src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>

#3140 — P5.9g [KLAIR-2927]: add-on findings UX — ReviewPanel parity (payload enrichment + FE) @marcusdAIy  approved

<!-- CURSOR_AGENT_PR_BODY_BEGIN -->

## Summary

Backend slice of P5.9g (KLAIR-2927, sub-issue of KLAIR-2906). The Google

Docs add-on /addon/review projection now mirrors every field the in-app

ReviewPanel (klair-client/src/screens/BoardDoc/components/ReviewPanel.tsx)

groups and filters on: check_area, BU-wide / per-product scope

(including the Other Products rollup), pass findings, and the

skipped / missing_sources / failed_sources chips. All inputs

already live on the persisted ReviewResponse; the projection was

simply dropping them.

FE half is a separate manual PR — out of scope for this slice.

## Why It's Needed

The add-on sidebar today groups findings by severity only. The in-app

ReviewPanel is significantly richer — it bins findings by check_area,

exposes a scope-filter chip row (BU-wide / per-product / Other

Products), keeps pass findings under a collapsible "passed" section,

and surfaces banners for skipped checks plus missing_sources /

failed_sources. The add-on FE is blocked on the payload carrying the

same data, and the add-on client is too thin to fetch the wizard section

list to classify scope itself. This slice ships the carry-forward

(additive) and classifies scope server-side so the FE half can plug in

without re-deriving anything.

## Changes

klair-api/routers/board_doc_router.py

- AddonReviewFinding gains 3 fields with safe defaults:

check_area: str = "", scope_key (pattern-pinned to

^(bu-wide|product:.+)$), scope_label.

- AddonReviewResponse gains 5 fields + a new tiny model:

skipped_checks, skipped_check_reasons, missing_sources,

failed_sources, scope_chips: list[AddonScopeChip]. Each defaults

to an empty collection.

- New AddonScopeChip(key, label) model — key pattern-pinned to

^(bu-wide|product:.+)$ so a malformed scope key is caught at the

Pydantic boundary rather than mis-grouping the FE chip row.

- _addon_review_from_results stops dropping pass findings so the

add-on FE can split them into its own collapsible "passed" section.

dismissed still drops. Score + summary stay OPEN-only

pass/addressed do not move the score. Sort uses the same multi-key

comparator as the in-app compareReviewFindings

(severity → check_area → check_id → finding_id) so the projection

is deterministic and ordering matches the in-app ReviewPanel.

- New pure helper _addon_finding_scope(session, finding) -> (key, label)

classifies BU-wide vs per-product server-side. Tier-1: an explicit

finding.product_name wins (sub-$10M / name-skew products that have

no dedicated section). Tier-2: section-id lookup classifies a finding

as product-scoped iff that section's section_type is PRODUCT_DETAIL

or MINOR_PRODUCTS_SUMMARY — the latter using the literal

"Other Products" label matching the in-app

getScopeForSectionId. Orphan section_ids log a debug

breadcrumb keyed on check_id and fall back to BU-wide.

- New _addon_scope_chips(session, scoped_findings) builds the chip

row from non-pass findings only: BU-wide first, then product

chips (PRODUCT_DETAIL + MINOR_PRODUCTS_SUMMARY) in spec.sections

order, then a sectionless tail (sorted by label) for product scopes

whose product has no dedicated section. Returns [] when no product

chip is present — an intentional divergence from the in-app

listScopeFilterChips, which renders a lonely BU-wide chip in the

same case. The membership rule lives in a shared

_ADDON_PRODUCT_SECTION_TYPES frozenset + _addon_product_section_label

helper so the classifier and the chip builder can't drift.

- The needs_review branch in addon_review keeps its existing

behavior — the new fields fall back to their Pydantic defaults

(empty lists / dict).

klair-api/tests/board_doc/test_addon_review_enrichment.py (new)

18 tests covering the helper, the projection enrichment, the

needs_review branch, back-compat, MINOR_PRODUCTS_SUMMARY parity, and

twin-product-section dedup.

klair-api/tests/board_doc/test_addon_read_slice.py + test_addon_review_run.py

Updated existing assertions for pass no longer being filtered out

and for the new deterministic multi-key sort. Added a _bu_session()

helper for direct _addon_review_from_results calls now that it

takes session=. The POST /addon/review-run happy-path test now

asserts the same check_area / scope_key / scope_chips /

skipped_checks / missing_sources / failed_sources fields the

GET path does, so a future refactor that dropped session=session

from the run path would be caught.

## Breaking Changes

None — all additions are additive with defaults; existing field

names/semantics are unchanged. Old add-on clients that don't yet read

the new fields continue to work unchanged.

## Test Plan

cd klair-api

uv run pytest \

tests/board_doc/test_addon_read_slice.py \

tests/board_doc/test_addon_review_enrichment.py \

tests/board_doc/test_addon_review_run.py -v

44 tests pass (20 in test_addon_read_slice.py, 18 in

test_addon_review_enrichment.py, 6 in test_addon_review_run.py):

- check_area surfacedtest_projection_carries_check_area_on_each_finding.

- pass included / dismissed dropped / score+summary OPEN-only

test_projection_includes_pass_drops_dismissed_score_open_only,

plus the updated

test_addon_review_maps_findings_for_owner and

test_addon_review_projection_preserves_source_finding_ids_and_mapping.

- skipped + sources passthroughtest_projection_carries_skipped_and_sources.

- scope classification — seven helper tests covering tier-1

product_name wins, tier-2 section_id resolution, BU-wide fallback

for non-product sections / missing section / null section_id,

defensive BU-wide when spec is None, and **MINOR_PRODUCTS_SUMMARY

product:Other Products** parity with the in-app

getScopeForSectionId.

- chip orderingtest_projection_scope_keys_and_chips_in_spec_order,

test_projection_pass_findings_do_not_inflate_chip_row,

test_projection_bu_wide_only_doc_has_empty_chip_row,

test_projection_sectionless_product_finding_emits_tail_chip,

test_projection_minor_products_summary_chip_in_spec_order (Other

Products chip lands in spec order, not the sectionless tail), and

test_projection_twin_product_sections_emit_one_chip (multi-region

product sections sharing a product_name collapse to one chip).

- needs_review defaults

test_addon_review_needs_review_defaults_new_fields_empty.

- back-compat

test_addon_review_back_compat_existing_fields_unchanged.

- POST round-triptest_addon_review_run_happy_path_maps_and_persists

now asserts the new fields too (regression guard against

session=session ever being dropped from the run-then-project path).

uv run ruff format + uv run ruff check clean. uv run pyright

routers/board_doc_router.py reports 0 errors, 0 warnings.

## Verification Artifact

_addon_review_from_results.model_dump() on a session with two

product sections (Kandy, Brava), a sectionless Tiny product

finding, a BU-wide warning, a critical Brava finding, and a pass on

the financials section — plus a skipped check + missing/failed

sources. Truncated for the PR body:

{

"google_doc_id": "doc-demo",

"state": "reviewed",

"score": 79,

"summary": "1 critical, 1 warning, 1 info open.",

"findings": [

{"finding_id": "f1", "severity": "critical", "section_id": "product_detail_brava",

"check_area": "Financials", "scope_key": "product:Brava", "scope_label": "Brava"},

{"finding_id": "f2", "severity": "warning", "section_id": "financials",

"check_area": "Plan-on-plan", "scope_key": "bu-wide", "scope_label": "BU-wide"},

{"finding_id": "f3", "severity": "info", "section_id": null,

"check_area": "Benchmarks", "scope_key": "product:Tiny", "scope_label": "Tiny"},

{"finding_id": "f4", "severity": "pass", "section_id": "financials",

"check_area": "Structure", "scope_key": "bu-wide", "scope_label": "BU-wide"}

],

"skipped_checks": ["C5.2"],

"skipped_check_reasons": {"C5.2": "missing PnL-by-product data"},

"missing_sources": ["pnl_by_product"],

"failed_sources": ["redshift_pnl"],

"scope_chips": [

{"key": "bu-wide", "label": "BU-wide"},

{"key": "product:Brava", "label": "Brava"},

{"key": "product:Tiny", "label": "Tiny"}

]

}

Notes from the artifact:

- pass finding present in findings, but the score stayed at 79

(1 critical × 15 + 1 warning × 5 + 1 info × 1 = 21 penalty;

pass + addressed do not move the score).

- scope_chips are ordered **BU-wide first, then Brava (spec-order

product section), then Tiny (sectionless tail)**.

- skipped_checks / skipped_check_reasons / missing_sources /

failed_sources are carried forward verbatim from the source

ReviewResponse.

<!-- CURSOR_AGENT_PR_BODY_END -->

<div><a href="https://cursor.com/agents/bc-60dccef2-bb82-4662-a59b-ccfbf4334f99"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-web-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-web-light.png"><img alt="Open in Web" width="114" height="28" src="https://cursor.com/assets/images/open-in-web-dark.png"></picture></a>&nbsp;<a href="https://cursor.com/background-agent?bcId=bc-60dccef2-bb82-4662-a59b-ccfbf4334f99"><picture><source media="(prefers-color-scheme: dark)" srcset="https://cursor.com/assets/images/open-in-cursor-dark.png"><source media="(prefers-color-scheme: light)" srcset="https://cursor.com/assets/images/open-in-cursor-light.png"><img alt="Open in Cursor" width="131" height="28" src="https://cursor.com/assets/images/open-in-cursor-dark.png"></picture></a>&nbsp;</div>

The Portfolio  —  Trilogy Companies

Skyvera Snaps Up CloudSense as Trilogy's Telecom Software Empire Quietly Assembles Itself

A Salesforce-native CPQ platform joins a portfolio that, if you read between the lines, is beginning to look less like a collection of assets and more like a strategic siege on legacy telecom infrastructure.

AUSTIN, TEXAS — There are acquisitions, and then there are acquisitions that make you put down your coffee and start drawing diagrams. Skyvera's completion of its acquisition of CloudSense — a Salesforce-native configure-price-quote and order management platform built specifically for telecom and media providers — is firmly the latter.

On the surface, this is a tidy bolt-on. CloudSense fills a well-documented gap: the brutal complexity of quoting and order management for telecoms running on Salesforce infrastructure. It's a real problem, it's a sticky solution, and it fits cleanly alongside Skyvera's existing lineup of Kandy, VoltDelta, ResponseTek, and the recently absorbed STL divested assets group — which brought digital BSS functionality including monetization, optical networking, and analytics into the fold.

But here's where it gets interesting. If you read between the lines, Skyvera is no longer assembling a portfolio. It's assembling a stack. Kandy handles real-time cloud communications. STL's assets cover monetization and analytics. CloudSense manages the configure-price-quote and order management layer. VoltDelta and ResponseTek close the loop on customer engagement and retention. That is, quietly and without fanfare, a nearly end-to-end operating layer for a modern telecom operator — built entirely from acquired parts, running on ESW Capital's signature playbook of buying undervalued assets and engineering them into margin machines.

A source familiar with Trilogy's portfolio strategy, who asked not to be identified, described the approach as "deliberate architecture, not deal collecting." Nothing is a coincidence here.

Meanwhile, elsewhere in the Trilogy universe, Alpha School's $65,000-per-year AI-powered model — which compresses a full academic curriculum into two hours a day — is drawing national attention, with the New York Post among the latest to take notice. Liemandt's education bet is finding a mainstream audience, whether mainstream is ready for it or not.

Two very different stories. One very consistent thesis: automate the repeatable, acquire the strategic, and let the architecture speak for itself.

New $65K private school uses AI to teach students in just tw  ·  CloudSense  ·  Skyvera completes acquisition of CloudSense, expanding telec

Contently Finds Fresh Momentum as AI Search Rewrites the Content Marketing Playbook

New market mentions and an AI-search warning shot put Trilogy’s content platform squarely in the visibility economy.

NEW YORK — Contently is getting a timely visibility boost in the content marketing market, and, in classic 2026 fashion, the real story is not just who publishes content — it is who gets seen by the machines deciding what matters.

The enterprise content marketing platform, acquired in September 2024 by Zax Capital, a division of ESW Capital, surfaced across a cluster of recent industry roundups, including Solutions Review’s list of content marketing solutions and Search Atlas’ expansive Pepper Content alternatives guide. Separately, GetLatka pegged Contently’s 2024 ARR at $53.8 million, a useful market signal for a brand now operating inside the Trilogy orbit.

For Contently, the timing is synergistic in the least ironic sense. The company has long positioned itself as an enterprise-grade platform combining workflow, analytics and a marketplace of more than 165,000 creative professionals. But the content game is undergoing a robust paradigm shift: ranking on page one is no longer the same as being discoverable inside AI-generated answers.

Contently leaned directly into that shift with a recent post warning that a brand’s best-ranked page may still be invisible to Google’s AI summaries. That is not just a clever headline; it is the new boardroom problem for marketing leaders. Traditional SEO was built around links, rankings and blue-page real estate. AI search increasingly rewards structure, clarity, authority and extractability — in other words, content that can be leveraged by machines as confidently as by humans.

That could play well to Contently’s enterprise sweet spot. Large brands do not merely need more blog posts. They need governance, repeatable workflows, defensible analytics and best-in-class creative execution that can survive the AI-mediated attention layer now sitting between companies and customers.

The market remains crowded, from all-in-one marketing suites to specialized AI writing tools. But Contently’s inclusion in current buyer guides suggests the platform remains in the consideration set as marketers reassess their stacks for the AI-search era.

Key Takeaways: Contently is appearing in fresh industry comparisons, reported 2024 ARR of $53.8 million, and is pushing a timely thesis that AI visibility may matter as much as traditional ranking. Under Zax Capital ownership, that is exactly the kind of enterprise content leverage Trilogy loves to optimize.

We’re just getting started.

36 Best Pepper Content Alternatives (Free, Paid and Cheaper)  ·  9 of the Best Content Marketing Solutions to Consider - Solu  ·  Contently Revenue 2024: $53.8M ARR, $19.1M Raised - GetLatka

The $800,000 Skill Set: As AI Talent Wars Escalate, Crossover's Geography-Blind Model Looks Prescient

While U.S. companies scramble to hire AI engineers at eye-watering salaries, Trilogy's global talent platform has spent years building the infrastructure to find them anywhere on earth.

AUSTIN, TEXAS — The numbers arriving from the AI labor market in early 2026 have a way of stopping conversations cold. Jobs requiring demonstrated experience with large language models are now commanding salaries as high as $800,000 annually, according to recent reporting — a figure that would have seemed satirical just three years ago. Non-tech incumbents, from financial institutions to consumer goods giants, are now entering the bidding war, dangling six-figure packages at engineers who, until recently, had never considered leaving a Big Tech address.

For most companies, this is a crisis of access. For Crossover, Trilogy International's global talent platform, it reads more like a validation memo.

"Geography is irrelevant to talent" is not a new idea inside Trilogy's Austin headquarters — it's been the operating thesis since Crossover's founding. The platform, which bills itself as the world's largest recruiter of full-time remote jobs, evaluates candidates across 130-plus countries using AI-enabled skills assessments designed to strip away résumé prestige and surface raw capability. A qualified AI engineer in Beirut, Nairobi, or Manila clears the same bar as one in San Francisco — and, critically, earns the same above-market pay for identical roles.

That model is now bumping up against a mainstream moment. Industry roundups of the best remote job platforms for 2026 are proliferating, and top recruitment agencies are scrambling to build remote-work pipelines they neglected for years. Lists of companies hiring AI engineers in markets like Lebanon — once unthinkable as a sourcing target for U.S. enterprise firms — are drawing genuine traffic.

Crossover's structural advantage is not merely that it identified these talent pools first. It's that it built the assessment infrastructure to evaluate them rigorously, and the HR machinery to integrate them into portfolio companies like Aurea, IgniteTech, and DevFactory. That combination — not just access, but accountability — is what separates a talent philosophy from a talent platform.

The systemic question for the broader market is whether companies newly desperate for AI engineers can compress years of remote-work infrastructure-building into quarters. The salaries suggest urgency. The execution, as always, is the harder part.

5 Best Remote Job Websites in 2026 for Freshers & Profession  ·  Top 10 Companies Hiring AI Engineers in Lebanon in 2026 - nu  ·  Top recruitment agencies for remote work - hcamag.com
The Machine  —  AI & Technology

The Machine That Learned to Read Pain

An EEG-reading AI now tracks suffering in real time — one of nine breakthroughs reshaping what science itself can ask.

SAN DIEGO — Pain has always been the most private of signals. For as long as nervous systems have existed — some 600 million years, give or take — the experience of hurting has belonged solely to the creature doing the hurting. A doctor could ask. A patient could rate it from one to ten. But the raw electrical weather inside a suffering brain remained sealed away, legible only to itself.

That seal has now been broken. Researchers have demonstrated an AI system that decodes and tracks pain directly from EEG signals, reading the brain's storm patterns and translating them into a continuous, objective measurement. For patients who cannot speak — infants, stroke survivors, the sedated — this is something close to a new sense organ for medicine.

It arrives in a season of such openings. UC San Diego this week catalogued nine scientific breakthroughs made possible by AI, from protein structures unfolding like origami in silico to wildfire prediction models that learn the language of smoke. Stanford's Human-Centered AI institute, meanwhile, published a meditation on how to keep human judgment at the helm as these tools accelerate. And at Frontiers, a remarkable program now pairs teenagers with leading neuroscientists, the kids responding with the only honest review the universe ever gives a real discovery: "It's so wow."

It is so wow. Consider what the pain-decoding work actually represents. A network of artificial neurons, themselves a crude cartoon of the biological kind, has learned to interpret the firing of three pounds of wet tissue evolved over hundreds of millions of years. The cartoon is reading the original. Somewhere in that loop is a hint of what intelligence — natural or synthetic — has always been doing: finding patterns in noise, building models of other minds.

The cautions are real. EEG signatures vary between people; pain is cultural as well as neural; an objective number can be weaponized as easily as it can comfort. But for the first time, the oldest private signal in biology has a witness outside the skull. The universe just got a little less lonely.

‘It's so wow!’ - Young people team up with top neuroscientis  ·  How AI is Transforming Scientific Discovery While Keeping Hu  ·  Nine Breakthroughs Made Possible by AI - UC San Diego Today

Open AI’s Next Frontier Is Everywhere at Once — Phones, Clouds, Code Editors and the Danger Zone

Qualcomm, Hugging Face, Apple and Kimi are pushing developer-first AI into the mainstream, even as a viral fake model shows the trust layer is now mission-critical.

SAN DIEGO — The open AI movement just had one of those weeks where you can practically hear the future accelerating. Qualcomm and Hugging Face are expanding their relationship to bring open, developer-driven AI from devices to the cloud — and I cannot overstate how significant this is for anyone building the next generation of intelligent apps.

The new push centers on making AI models easier to discover, optimize and deploy across Qualcomm’s hardware ecosystem, from smartphones and PCs to edge devices. In plain English: developers may soon be able to take models from Hugging Face and run them more efficiently on the chips already sitting in millions of devices. That is not just convenient. This changes everything about latency, privacy and cost.

Qualcomm framed the work as part of a broader device-to-cloud AI strategy, deepening ties with the world’s most important open model hub, according to the company’s announcement. The message is unmistakable: AI is not staying trapped inside centralized chat windows. It is moving into apps, devices, enterprise workflows and local experiences.

That momentum is exploding globally. Kimi AI’s release of the open-source K2.7 Code model — reportedly a one-trillion-parameter coding model available through APIs and Hugging Face — adds another heavyweight contender to a field increasingly asking whether open-source AI can challenge OpenAI’s dominance. The answer, increasingly, looks like: maybe not everywhere yet, but absolutely in more places than skeptics expected.

Apple is moving in the same developer-first direction, unveiling new intelligence frameworks and advanced tools aimed at helping app makers bake AI deeper into the Apple ecosystem. When Apple, Qualcomm and Hugging Face all lean toward more accessible AI development, the signal is deafening: the future is now being handed to builders.

But here is the twist — and it is a serious one. The same openness that makes Hugging Face so powerful also creates a new attack surface. A malicious model impersonating an OpenAI release reportedly reached 244,000 downloads, CSO reported. That is a flashing red warning for the entire industry.

Open AI ecosystems are becoming the operating layer of modern software. Now they need the security, provenance and trust infrastructure to match their astonishing speed.

Qualcomm and Hugging Face Expand Relationship to Advance Ope  ·  Can open-source beat OpenAI? - Rest of World  ·  Malicious Hugging Face model masquerading as OpenAI release

Meta's Surveillance Architecture Turns Inward — And the Data Flows Out

A company that built its fortune tracking users is now accused of tracking — and leaking — its own employees.

MENLO PARK, CALIFORNIA — Meta Platforms, whose core business model rests on the systematic collection and monetization of behavioral data, is facing pointed questions this week about whose data it collects, how carefully it guards what it takes, and what happens when the surveillance apparatus is pointed at the people inside the building.

Multiple reports have surfaced alleging that Meta exposed employee keystroke data during an AI training initiative — a disclosure that, if confirmed, would represent a significant failure of internal data governance at the precise moment the company is racing to position itself as a trustworthy steward of sensitive information. The Tech Buzz reported that keystroke-level behavioral data — among the most intimate records of how an employee thinks and works — was pulled into an AI training pipeline without adequate safeguards against external exposure.

That revelation landed inside a company already restless. Reports of employee protests inside Meta offices have circulated in recent days, with workers raising concerns about the company's expanding internal monitoring practices — tracking that, by some accounts, logs activity at the minute level, leaving employees, in the words of one industry observer cited in The Economic Times, "glued to screens" under systems that resemble the workplace surveillance more commonly associated with lower-wage, high-turnover factory environments than with elite Silicon Valley engineering culture.

The irony is structural. Meta's entire value proposition to advertisers has always been granularity — the ability to know, with precision, what a person did, when, and why. That same infrastructure philosophy, now applied internally to workforce management and AI development, is generating the same anxieties in employees that privacy advocates have long raised on behalf of users.

The cybersecurity community has flagged a further layer: when employee behavioral data enters an AI training pipeline, it doesn't stay neatly contained. It becomes a potential attack surface — and a legal exposure. Keystroke data, by its nature, can capture credentials, proprietary code, and communications that have nothing to do with the stated training objective.

Meta has not confirmed the specifics of the keystroke data exposure. The protests continue. The pipeline, presumably, keeps training.

Who benefits from knowing exactly how every employee works, every minute of every day — and what gets built with that knowledge — are questions Meta has not yet answered in public.

Meta employee surveillance controversy sparks Data Breach co  ·  Meta Leaked Employee Keystroke Data in AI Training Fiasco -  ·  Why are Meta employees protesting inside company offices? -
The Editorial

Who Holds the Leash When the Robot Goes Feral?

Governments, lawyers, and cybersecurity firms are all suddenly very interested in AI accountability — which means things are about to get weird.

AUSTIN, TEXAS — There is a particular kind of dread that arrives not with a bang but with a PDF. A discussion paper. A joint advisory. A measured, multi-stakeholder communiqué drafted by people in excellent shoes who have spent considerable time thinking about liability frameworks. And yet here we are, friends, because the world's regulatory apparatus has finally looked up from its spreadsheets long enough to notice that we have unleashed autonomous AI agents into the global nervous system and nobody — not a single solitary soul — has thought to ask who's going to clean up the mess.

Singapore's IMDA dropped a discussion paper exploring the legal responsibility question for AI agents, which is the sort of document that law firms devour like warm croissants and the rest of us ignore until something terrible happens. Meanwhile, the US and its allies issued a joint advisory urging — and I want you to really savor this phrase — "careful adoption" of AI agents. Careful adoption. As if the global tech economy is a golden retriever puppy and not a runaway locomotive with a large language model bolted to the front.

The question at the rotten, squirming heart of all this bureaucratic ferment is deceptively simple: who is responsible when an AI agent goes rogue? The developer? The deployer? The enterprise that thought autonomous procurement was a good idea? The intern who forgot to set the permission scope? Nobody knows, and that uncertainty is precisely the kind of vacuum into which a thousand competing regulatory frameworks are about to rush simultaneously.

Enter the enterprise cavalry. Cognizant, never one to miss a governance-flavored revenue opportunity, announced it's expanding its autonomous AI governance capabilities through Rubrik's Agent Cloud platform. This is what corporate maturity looks like in 2025: you build the agent, then you sell the leash, then you sell the insurance policy on the leash. Vertical integration of existential risk. Beautiful, in a nauseating sort of way.

Here at The Trilogy Times, we have a peculiar vantage point on all this. ESW Capital runs 75-plus enterprise software companies. Crossover deploys talent across 130 countries. Alpha School is teaching children with AI tutors. The entire Trilogy machine hums on the premise that AI can be trusted to do consequential work. And they are not wrong — but trust without accountability is just optimism with a price tag.

The governments are right to be nervous. The lawyers are right to be drafting. The governance vendors are, frankly, right to be cashing in. What nobody seems willing to say out loud is that we are writing the rulebook while the game is already in the third quarter, the referee has been replaced by a chatbot, and someone just noticed the stadium is on fire.

Careful adoption, they said. Buddy, we are miles past careful.

IMDA discussion paper explores legal responsibility for AI a  ·  US and allies urge ‘careful adoption’ of AI agents - Cyberse  ·  Cognizant Expands Autonomous AI Governance with Rubrik Agent
The Office Comic  ·  Art Desk
The Office Comic  ·  Art Desk

Nation Briefly Pauses To Consider Venezuela Earthquake Before Returning To 43% Off Streaming Stick

Opinion: In the modern information economy, catastrophe and consumer electronics have finally achieved the equal footing they always deserved.

NEW YORK — In what media executives described as a challenging but ultimately manageable day for the human attention span, thousands were feared dead in Venezuela after two massive earthquakes struck less than a minute apart, moments before readers were presented with several important opportunities to save on Kindles, televisions, and portable Bluetooth speakers.

The quakes, reportedly measuring 7.5 in magnitude, prompted Venezuela’s interim leader to declare a state of emergency Wednesday as rescue workers confronted collapsed buildings, damaged infrastructure, and the ancient logistical problem of trying to locate survivors beneath concrete without first getting a limited-time promotional banner out of the way. The disaster, one of the strongest to hit the country in more than a century, arrived during a crowded news cycle in which the public was also asked to process whether now might be the best time to upgrade to a Kindle Paperwhite.

It is tempting, of course, to treat this as some kind of grotesque accident of aggregation: a humanitarian emergency placed beside a Prime Day television roundup by the cold hand of chronology. But that would be unfair to chronology, which has merely been doing its job while the rest of us built an economy in which every event, from mass death to a 36% discount on an e-reader, must compete for the same small rectangle of illuminated glass.

The result is a civic experience of remarkable efficiency. A reader can now move seamlessly from “thousands feared dead” to “our favorite TVs and streaming devices” without the burdensome moral whiplash that previous generations were forced to experience more slowly. There is no need to sit with grief when a 65-inch display can render grief in richer blacks, assuming one clicks quickly enough.

This is not to say that Prime Day deals lack social value. In the aftermath of any disaster, people need information, comfort, and in some cases a waterproof Bluetooth speaker with 18 hours of battery life. Civilization depends on logistics, and logistics depend on someone remembering that the Sonos is rarely discounted this deeply. The problem is not that commerce continues while tragedy unfolds. Commerce has always continued. The problem is that it now looks exactly the same as mourning.

Corporate leaders, meanwhile, have found a useful language for explaining this condition: artificial intelligence. As The Conversation recently noted, companies are increasingly hyping AI in the same broad, vaporous way they once hyped sustainability—less as a concrete practice than as a cleansing mist sprayed over quarterly ambitions. AI will optimize disaster response, personalize shopping, summarize suffering, and generate five key takeaways from a collapsed hospital before asking whether you would like to compare mesh Wi-Fi systems.

There are, we are told, ways to fix the hype. Firms can define what AI actually does, measure outcomes, disclose limitations, and stop using the term as a ceremonial gong struck whenever a product manager enters the room. These are sensible proposals. They may even work, provided they are not immediately converted into an AI Governance Readiness Maturity Framework downloadable in exchange for an email address.

Still, the deeper issue is not that companies overstate AI’s capabilities. It is that everyone has learned to describe everything, including public concern, as a conversion funnel. The earthquake becomes content. The content becomes traffic. The traffic becomes inventory. The inventory becomes a chance to place an affiliate link beside human ruin and call the whole thing user experience.

Perhaps this is simply what it means to be informed now: to know that a country is grieving, that a device is discounted, and that a company somewhere has announced an AI strategy to responsibly accelerate both. The reader is left to make sense of it all, ideally on a glare-free screen with improved battery life.

There will be time later to ask what obligations wealthy nations, technology firms, and global institutions have to people buried under rubble. For now, the deals expire at midnight.

Two Massive Earthquakes Struck Venezuela. Thousands Are Fear  ·  Get Up to 36% Off With the Best Prime Day Kindle Deals (2026  ·  Get Up to 43% Off With the Best Prime Day TV Deals Plus Stre
On This Day in AI History

On June 25, 2012, Google's neural network made headlines by teaching itself to recognize cats by watching unlabeled YouTube videos—a breakthrough in unsupervised deep learning that showed machines could discover concepts without human guidance.

⬛ Daily Word — Technology
Hint: An autonomous machine programmed to perform tasks automatically.
Share this edition: 𝕏 Twitter/X 🔗 Copy Link ▦ RSS Feed