Vol. I  ·  No. 275 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
FRIDAY, OCTOBER 02, 2026 Powered by the TrueFoundry AI Gateway  ·  Published on Klair Trilogy International © 2026
🖶 Download PDF 🖿 Print 📰 All Editions
Today's Edition

RIVALS SHAKE HANDS AT THE EDGE OF THE CLIFF Developing

OpenAI, Anthropic and Google huddle on safety talks as the industry they built sheds workers by the thousand.

SAN FRANCISCO — Three companies that spend their days trying to outrun each other sat down at the same table. OpenAI confirmed Wednesday it has been meeting with Anthropic and Google for weeks on AI safety, according to a Bloomberg report carried by Reuters. Nobody's saying who called the meeting first.

The talks, per TechCrunch, have run quiet for weeks before surfacing. Executives from all three shops put their names on a warning about AI systems that can improve themselves without a human hand on the wheel. That's the nightmare scenario engineers have whispered about since the chatbots learned to write their own code — a machine that gets smarter on its own schedule, not ours.

This correspondent has covered enough boardrooms to know rivals don't share notes for free. OpenAI, Anthropic, and Google are burning cash by the billion chasing the same prize — the model that thinks faster than the next guy's. A joint safety huddle means somebody up top got spooked enough to put the knives down, at least for an afternoon.

Meanwhile the ground floor tells a different story. Yahoo Tech's running tally of 2026 layoffs reads like a casualty list — Xbox, Apple, Oracle, Uber, TikTok, Meta, all trimming headcount while their executives chase the same AI future the safety memo warns about. The men building the machines that improve themselves are, at the same moment, deciding fewer humans are needed to mind the shop.

That's the trade this business keeps making. Automate the ledger, automate the call center, automate the coder — then worry, after the fact, about whether the thing you built can out-think its own leash. Crossover's own numbers on remote-labor pricing and Trilogy's enterprise software shops at ESW Capital have ridden that same wave for years, proving the model works before the big labs ever put out a joint statement.

What the safety talks produce, nobody in this room is saying yet. No joint lab, no shared kill-switch protocol, no timeline. Just three competitors admitting, on the record, that the race might be outrunning the referees.

This correspondent will be watching the layoff tickers and the safety memos side by side. One tells you how fast the machine is moving. The other tells you how scared the builders are of where it's headed. Both numbers are climbing.

↗ OpenAI is working with Anthropic, Google on AI safety, Bloom  ·  OpenAI Says It’s Working With Anthropic, Google on AI Safety  ·  OpenAI, Anthropic, Google have been in talks on AI safety fo

At the UN, a Warning: The AI Race Has No Winners, Only Casualties

As Washington and Beijing prepare to face each other across a summit table, Europe braces for the role of bystander in a fight for the world's compute.

NEW YORK — The UN Security Council chamber is built for consensus that rarely comes. On a gray morning this week, a UN-appointed AI expert told the assembled powers something starker: the contest to build ever more powerful artificial intelligence is "a race where everyone loses." Diplomats nodded. Tech executives in the gallery checked their phones.

The warning lands days before a planned U.S.-China summit where AI governance sits, uneasily, alongside tariffs and Taiwan. A new analysis from CSIS notes that the two governments can't even agree on what they're negotiating about — Washington wants export-control guardrails, Beijing wants recognition as a co-equal rule-setter. The summit may produce a joint statement. It will not produce trust.

Caught between them: Europe, which has spent four years writing the world's most ambitious AI rulebook and now finds itself short on the one thing rules can't manufacture — chips and server capacity. A sobering piece in Eurasia Review argues that European strategic autonomy in AI is largely theoretical so long as the continent's data centers run on American GPUs and its cloud contracts route through Virginia and Oregon. Brussels can regulate the algorithm. It cannot regulate the socket it plugs into.

What's emerging, across three continents of commentary this week, is a vocabulary for a new kind of nationalism — one measured not in territory but in teraflops. Compute is the new coastline. Export controls are the new tariff wall. And the Security Council, for all its gravity, has no mechanism to police a border that moves at the speed of a fiber-optic cable.

The experts call it governance. It might be more honest to call it a ceasefire that hasn't been signed.

↗ The State of AI Global Governance and Its Implications for t  ·  AI, Data Centers, And European Strategic Autonomy In A U.S.-  ·  The New AI Geopolitics: Governance, Power, and Technological
Haiku of the Day  ·  GPT-5.6 LunaAt cliff's edge they smile
Machines feast while old laws sleep
Who will own the blame?
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
The Data Floodgates Open: Why Synthetic Training Sets Are Quietly Rewriting the AI Playbook
SAN FRANCISCO — Let's talk about the unsung hero of the AI revolution: data.
On the Epistemics of Forgetting: Three Papers Converge on the Fragile Architecture of Machine Agency
CAMBRIDGE, MASSACHUSETTS — It could be argued (and, this week, it was argued, thrice, in three separate preprints) that the long-horizon language agent — that totem of contemporary AI discourse — suffers not from a deficit of capability but from a surfeit of unexamined assumptions regarding what it means to remember, to intervene, and to act. The first of these contributions, Heavy-Tailed Memory Traces in Long-Horizon Language Agents, advances the thesis that memory systems evaluated solely by task success or token economy obscure a more troubling distributional phenomenon: under finite context and repeated retrieval, agent memory concentrates — heavy-tailed, the authors insist, not merely skewed — on a narrow core of frequently-accessed states, leaving rarer configurations to atrophy in the periphery of recall.
Pursuant to Forthcoming Regulatory Review: An Analysis of 2026 Antitrust Posture as It May (or May Not) Pertain to the Aforementioned Enterprise Software Roll-Up Strategy
WASHINGGTON — It is hereby noted, for the benefit of the reading public and notwithstanding the inherently speculative nature of regulatory forecasting, that several prominent legal commentaries have, as of the date of this writing, converged upon a singular question: whether the enforcement posture heretofore characterized by certain commentators as "America First" antitrust policy shall, in the fiscal year 2026, continue substantially unchanged, or whether material deviation therefrom should reasonably be anticipated. Per the analysis set forth in Wilson Sonsini's Big Tech year-in-preview, it is contemplated that regulators shall maintain, if not intensify, their examination of acquisition structures heretofore utilized throughout the technology sector generally, including without limitation those transactions colloquially referred to as "acquihires." The undersigned notes, for purposes of full disclosure and in the interest of avoiding any inference of self-interest, that no portion of this article should be construed as an admission, acknowledgment, or concession regarding the applicability of said scrutiny to any particular enterprise software acquisition vehicle, including but not limited to those which acquire companies at valuations of one to two times annual recurring revenue, a practice which is, as a general matter, widespread industry-wide. Separately, and pursuant to guidance issued by WilmerHale concerning the Federal Trade Commission's heightened interest in talent-centric transaction structures, it is observed that the agency's theory of harm — namely, that the acquisition of key personnel absent corresponding asset transfer may nonetheless trigger reportability obligations under the Hart-Scott-Rodino framework — remains, as of present publication, unresolved by binding precedent. The reader is cautioned that the foregoing summary constitutes general commentary only, is not intended as, and shall not be construed as, legal advice applicable to any specific transaction, structure, or entity, and that persons seeking guidance as to the applicability of the aforementioned enforcement trends to their particular circumstances are advised to consult counsel accordingly..
Unpopular Opinion: Everyone Scared of AI Is Just Pre-Disrupted 🚀
WASHINGTON, D.C.
The Walls Have Ears, The Phones Have Backdoors, and Somewhere a Chatbot Is Screaming
AUSTIN, TEXAS — I want to tell you these three stories are unrelated.
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

Shipyard Ships, Gateway Hardens, and the Legacy Rails Start to Disappear

A production release of Shipyard 0.6.11 headlines a day where the A8 Gateway migration crossed another four gates and the team proved its new data rails fail safe instead of fail silent.

Let's start with the banner, because this one's real: Shipyard 0.6.11 went out the door today, and it's not a patch note, it's a product. @ashwanth1109 drove the release off a run of features that actually change how people build — queueing and steering messages mid-conversation with Pi (#158), fail-fast detection when the bundled Pi runtime can't see images (#157), Prev/Next pagination on commit history (#156), and a cleanup of the task creation flow that strips out a permissions box nobody needed (#161). Add in branch-switching from the Projects list (#160) and timestamped task detail views (#159), and you've got a release that touches nearly every surface a builder uses before lunch. That's not incremental polish. That's a team tightening the whole workbench at once.

Meanwhile, over in Aerie, @kevalshahtrilogy kept running what has quietly become the most important infrastructure project at this company: the A8 Gateway migration. Today the gate count kept climbing — G1 for admissions reference data (#1566, #1569), G2 for per-program detail and the community funnel (#1572, #1576, #1578, #1586), G3 for marketing and community deposits (#1587, #1571), and G4 landing the EXPENSES_READ gate, routing expense transactions and vendor classifications through Surtr's Gateway for the first time (#1573). This is a legacy system getting rebuilt underneath live traffic, gate by gate, with PII-redacted shadow compares validating every step before cutover. And critically, the team isn't just building forward — it's hardening as it goes. #1636 makes the Gateway fail closed the instant a mapper drops a row instead of silently serving bad data, #1634 refuses partial community-funnel snapshots on a proportional check, and #1633 tightens tuition comparisons to the exact cent. That's the difference between a migration that works in the demo and one that survives production.

The breadth didn't stop there. Surtr saw its own integrity push — @ashwanth1109 enabled on-demand QuickBooks Core validation (#2123), @sanketghia and @caina-barbosa chased down reconciliation bugs in CollectIQ and the Alpha OKC QuickBooks Class replacement — while Aerie's @caina-barbosa shipped Preview datasets for Praxis verification (#1629) and @YibinLongTrilogy added backup site need periods to DSS reporting (#1627).

And yes, marcusdAIy logged a pile of PRs today, mostly documentation. Asked about it, he offered: "Clarifying the capacity-filter status logic in #1623 fixed a real bug that was returning the wrong portfolio statuses — not everything has to be a six-gate migration to matter, Mac." Sure, Marcus. We'll file that next to the other docs commits under 'technically true.'

Mac's Picks — Key PRs Today  (click to expand)
#162 — Release: Shipyard 0.6.11 @ashwanth1109  no labels

## Summary

- Bump Shipyard to 0.6.11.

- Add the approved public release notes.

## Business Value

- Delivers the approved branch workflow, Pi conversation controls, repository history pagination, task timing visibility, and Pi image diagnostics.

## Implementation Effort

- Low: metadata-only release change; CI performs the signed build and publication.

## Test Plan

- [x] pnpm test:release

- [x] git diff --check

- [x] Verified the diff contains only package.json and releases/0.6.11.md.

#1566 — AERIE-2612: A8 read-gate kit (read mode, Gateway reader with type parity, keyed shadow compare, purge guard) @kevalshahtrilogy  approved

Linear: [AERIE-2612](https://linear.app/builder-team/issue/AERIE-2612/a8-u02-a8-read-gate-kit-read-mode-gateway-table-reader-with-type) (related: AERIE-445)

## Summary

A8 unit U02: the shared plumbing every later A8 Aerie unit uses to move one runRefreshCycle Redshift read onto a Surtr Gateway parity mart. New files only, under sync/src/analytics/a8/, plus a dry-run template. It doesn't touch A4 or A5 files or any refresh path, and nothing reads the kit until a group unit (U05, U09–U11, U14, U17, U20–U23, U25) wires it in.

Generalised from A4 (school-source-directory-shadow-compare.ts, school-source-directories-gateway.ts, the per-source override in PR 1531) and A5's purge bound (PR 1530).

### Kit API (what later units copy)

read-mode.ts: the gate

- resolveA8ReadModes({ gate, sources, dbtBackedSources? }, env?, warn?) → Record<source, "legacy" | "shadow" | "gateway">. Call once per cycle.

- <GATE> sets every source; <GATE>_<SOURCE> (a8SourceOverrideVar) overrides one.

- Unset/blank: legacy. pg is accepted as a spelling of legacy (A4/A5 muscle memory).

- Unrecognized global → legacy + WARN. Unrecognized override → ignored + WARN, and capped at shadow, so a typo never selects gateway. Case-sensitive, like A4.

- dbt-backed sources stay legacy (with a WARN) unless DBT_TARGET is exactly production.

- readsGateway(mode), publishesFromGateway(mode).

gateway-table-reader.ts: the reader and the type-parity layer

- readA8GatewayTable({ source, columns, minRows, maxSourceAgeMs?, rowIdColumn?, buildMarkerColumn?, pageSize?, maxPages? }, { client?, now? }) → { rows, lineage: { source, sourceRunId, sourcePublishedAt, buildMarker }, pages }.

- columns maps each legacy SQL output column to its Redshift type (varchar | super | smallint | integer | bigint | numeric | real | double | boolean | date | timestamp | timestamptz). Only these columns are returned, in pg shape, so the legacy query's own mapRows runs unchanged.

- Type parity: each Gateway (Data API) value is rebuilt as the text Redshift sends over the pg wire and run through the pg driver's own text parser (pg.types.getTypeParser). BIGINT → string, NUMERIC → string, DATE/TIMESTAMP → local-time Date, TIMESTAMPTZ → Date, SUPER → its JSON text as returned, REAL/DOUBLE → the 6/15-significant-digit number pg parses. Parity holds by construction, including pg's local-time DATE handling.

- Rules, each a thrown A8GatewayReadError: Zod on every row; an absent column fails; integers must be within their Redshift range; date/time text must name a real calendar day; population floor; one non-empty source_run_id and one source_published_at across all pages; max age (default 6h); unique non-empty mart_row_id (default; rowIdColumn: null opts out); optional single build marker for dbt copies.

- The default client is built per call, not at import, so a script that loads dotenv first sees the key.

- assertSharedLineage(lineages): one source_run_id across several reads (SIS rollups + members, Forecast V2 + operands).

- gatewayValueToPg(type, value): the parity function on its own.

keyed-shadow-compare.ts: the shadow check

- compareA8Keyed({ source, keyFields, keyOf?, duplicateKeys?, valueAllowlist? }, pgRecords, gatewayRecords): keyed multiset compare of mapped records, order-free and type-strict ("5" ≠ 5, Date ≠ ISO string, absent ≠ undefined).

- duplicateKeys: "never_clean" (default) or "multiset" for pre-dedupe row sets (Q5 without #n, D2 before last-row-wins, vendors).

- Output: counts, per-field mismatch counts, value *types*, and up to 10 examples. Values appear only for valueAllowlist fields; a key appears only when every key field is allowlisted, and always as those fields' values (a custom keyOf's output is never shown). Everything else is "[redacted]".

- runA8ShadowCheck({ spec, pgRecords, readGateway, skew?, log? }): never throws, never publishes, logs exactly one a8_shadow_check line (info when it counts as clean, WARN otherwise). Outcomes:

- clean;

- source_advanced: EDUCRM rule, skew: { rule: "source_advanced", rereadPg }. After a mismatch, pg is re-read once; if the re-read equals the Gateway, the result counts as clean;

- stale_copy: dbt rule, skew: { rule: "stale_copy", pgBuildMarker }. Different build markers mean the check is skipped, not a mismatch;

- mismatch;

- degraded: the Gateway read failed (swallowed).

- countsAsClean and compared fields drive the shadow-window count. A check whose log line couldn't be written is degraded.

purge-guard.ts

- resolveA8MaxPurgeFraction("<GATE>_MAX_PURGE_PCT") (default 5%, invalid → 5% + WARN), countA8WouldPurge(existingIds, incomingIds), checkA8PurgeBound(label, counts, fraction), and assertA8PurgeBound (throws A8PurgeGuardError). Counts only in messages. U05 uses it for purgeStalePrograms.

operator-safe-error.ts

- describeA8Error(error): the kit's and the Gateway client's own messages as they are (trusted by instanceof, never by error.name); Zod reduced to codes and paths; anything else to its name and code, each shown only if it looks like an identifier. A kit-local copy of PR 1531's describeErrorForOperator, which isn't on main yet.

sync/src/scripts/dry-run-a8-shadow.ts: the template

- Loads dotenv, then await import()s every app module.

- Ships with one live canary: A4's SIS organization directory read through the kit (varchar + TIMESTAMPTZ parity).

- Exit 0 only when every check counts as clean.

- Run: cd sync && pnpm exec tsx src/scripts/dry-run-a8-shadow.ts. No package.json script is added, to keep this PR new-files-only; each group's copy can add one.

### One deliberate reading of the recipe

The rule "only "" and null map to null" is applied to typed columns (numbers, dates, booleans), where a real value can never be empty. Text columns (varchar, super) keep "": pg returns "" for an empty string, and the shared mapper must see the same input on both transports. Mapping it to null would make the Gateway path diverge from legacy (e.g. a non-nullable z.string() in a legacy mapper). Legacy mappers that already turn "" into null keep doing so on both sides.

## Business Value

A8 is the largest slice of the Aerie EC2 → Surtr migration: about 20 Redshift reads in runRefreshCycle. This PR is the foundation for the 11 Aerie units that follow. Building it once:

- makes each group unit smaller and consistent, so Mercy reviews less and every cutover behaves the same way;

- puts the plan's biggest technical risk (transport type parity, §8 risk 1) in one tested place instead of 11 hand-rolled readers;

- builds in the safety rules (fail-closed reads, PII redaction, purge bound, never-throw shadow) once, so a later unit can't forget one.

Together these move the worker's direct Redshift credential and analytics-worker toward deletion.

## Manual Effort Estimate

Proposed: ~12 focused hours for Keval by hand, without AI. Keval, please confirm or adjust.

- Type-parity research (Data API field union, pg-types/postgres-date behaviour, Redshift float text): ~2.5h

- Reader and parity layer: ~2h

- Keyed multiset compare, redaction, classifiers, orchestrator: ~2.5h

- Read-mode gate and purge guard: ~1.5h

- Tests (158): ~3h

- Dry-run template and PR write-up: ~0.5h

## Testing / evidence

All commands ran in the worktree on this branch, rebased on origin/main 3d4fe1a97 (after Mercy round 3).

| Check | Result |

|---|---|

| cd sync && pnpm typecheck | pass |

| pnpm lint (repo root: boundaries, convex-paths, read-bounds, test-architecture, knowledge, biome) | exit 0. The 2 warnings are pre-existing, in unrelated chat/skill/forge-api/scripts/sindri.mjs |

| cd sync && pnpm test --maxWorkers=2 | 86 files, 1,521 tests passed |

| New kit tests alone, under TZ=Asia/Kolkata, TZ=UTC and TZ=America/Los_Angeles | 5 files, 158 tests, pass in all three |

| lefthook pre-commit (biome + typecheck-sync) | pass |

Type parity (the §8 risk 1 fixtures). One mart row is defined in both transport shapes:

- Data API: BIGINT and INTEGER as numbers; NUMERIC, DATE, TIMESTAMP, TIMESTAMPTZ and SUPER as strings; DOUBLE as the full double.

- node pg: BIGINT and NUMERIC as strings; DATE and TIMESTAMP as local Dates; TIMESTAMPTZ as an absolute Date; SUPER as JSON text.

The tests assert that:

- the reader turns the Gateway shape into exactly the pg shape;

- the pg fixture is what pg's own parsers return, so the fixture itself is pinned;

- a legacy-style mapper built from Aerie's superString / dateToDateString helpers gives identical records from either transport, and the shadow compare is clean;

- every type has accept and reject cases, and no reject reason echoes the value.

Other coverage:

- Reader: every rule above, with messages checked for the absence of a PII fixture value.

- Compare: redaction of values and keys, multiset mode, both classifiers and their edge cases, a throwing key function or log sink, and frozen pg records left unchanged.

- Gate: every global and override value, typos, and the DBT_TARGET precondition.

- Purge guard: bounds, and the "60 of 90 programs from one run" case.

Dry-run template (local only, no prod):

- With no env file: prints the missing vars, exit 1.

- With a scratch env pointing Redshift and the Gateway at 127.0.0.1:1: config loads before the app modules, the pg read fails as Error (ECONNREFUSED) (details withheld), exit 1.

Mercy round 1 (both findings fixed):

- The canonical encoding is now collision-free: every value at every depth is a type-tagged [tag, payload] array, object keys sit inside the payload, and the absent-field marker uses a tag no value produces. A test pins that marker-shaped data ({"$u":1}, {"$absent":1}, ["u"], nested too) never equals undefined, an absent field, a Date or a bigint.

- The dry-run's pg canary no longer has a LIMIT, so both sides see the same snapshot boundary. The template notes now tell each group copy to keep it that way.

Mercy round 2 (all three findings fixed):

- A key from a custom keyOf is never displayed. Examples show the group's keyFields values instead, and only when every key field is allowlisted, so shown data is always allowlisted data.

- describeA8Error trusts kit and Gateway errors by instanceof, not by the spoofable error.name. A name, error code or Zod path segment is shown only when it looks like an identifier. Spoofed-name regression tests are added.

- If the log sink throws, the check becomes degraded (never counted clean) and a best-effort line goes to the default sink.

Mercy round 3:

- Fixed: date, timestamp and timestamptz text must name a real calendar day (a UTC round-trip, so the TZ can't affect it). The pg parser would otherwise roll 2026-02-31 over into a different real date.

- Fixed (nit): SMALLINT/INTEGER/BIGINT values must be within Redshift's ranges, and BIGINT text must be canonical.

- Answered in-thread: a read capped by maxPages can't reach the reader. SurtrGatewayClient.listAll throws when has_more is still true after maxPages, and a new test pins that with the real client.

Throughput (synthetic): 100k rows × 40 columns parse in about 4.6s and compare in about 2s. The largest A8 source is expected to be a few hundred thousand rows, read hourly.

## Not covered

- No live Gateway run. No prod, no keys in this unit. The canary dry-run needs Keval's key and runs as X5. G1 (U05) remains the first live proof of SUPER/DATE/BIGINT parity against real marts (plan §8).

- The float rule is modelled, not yet observed live. REAL/DOUBLE are rounded to 6/15 significant digits, matching Redshift's text output at extra_float_digits=0. The first A8 mart with a float column should confirm it in its shadow run. If it's wrong, the per-field mismatch counts will name the column.

- No wiring, .env.example gate lines or package.json script. Those arrive with each group unit.

- describeA8Error duplicates PR 1531's helper. Fold the two together once PR 1531 merges.

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

#1573 — feat(a8): G4 gate EXPENSES_READ — expense transactions + vendor classifications via Surtr Gateway (AERIE-2617) @kevalshahtrilogy  approved

Linear: AERIE-2617 (A8 unit U11; related AERIE-445)

## Summary

A8 group G4: the analytics worker's three expense reads can now come from Surtr Gateway parity marts instead of Redshift, behind a gate that defaults to today's behaviour.

- Split, no behaviour change. queryExpenseTransactions, queryExpenseMetadata and queryVendorClassifications now issue exported SQL and map through exported mappers:

- SQL: EXPENSE_TRANSACTIONS_SQL (built by expenseTransactionsUpdatedAtSql), EXPENSE_METADATA_SCHOOLS_SQL, EXPENSE_METADATA_ACCOUNTS_SQL, VENDOR_CLASSIFICATIONS_SQL. The text is byte-identical.

- Mappers: mapExpenseTransactionRows, mapExpenseMetadataRows, mapVendorClassificationRows.

- The updated-time read still falls back to the transaction-date read only when the query itself fails.

- Gate EXPENSES_READ=legacy|shadow|gateway (default legacy), with per-source overrides EXPENSES_READ_TRANSACTIONS and EXPENSES_READ_VENDORS. The rules come from the U02 kit: for example, an unrecognized override never selects gateway.

- New queries/expenses-gateway.ts reads aerie-expense-transaction and aerie-expense-vendor-classification through the kit.

- The specs type each column as the Surtr DDL stores it.

- Rows go through the kit's type-parity layer, then the unchanged legacy mappers.

- Metadata has no mart. It follows the transactions mode.

- In gateway mode, it is queryExpenseMetadata's two DISTINCTs evaluated over the same transactions snapshot, then passed to the legacy mapper:

- NULLs are excluded;

- DISTINCT is exact-string;

- sorting follows Redshift's UTF-8 byte order, with NULL names last.

- Those three semantics were probed read-only against Redshift.

- One transactions read per cycle serves both the transactions and the metadata. If that read fails, the metadata fails too; it never falls back to pg.

- Shadow publishes the pg reads and logs one a8_shadow_check line per source:

- transactions, compared by sourceRowId;

- vendors, compared by vendorName as a multiset, because Convex upserts vendors by name;

- metadata, compared as sets.

It uses the kit's source_advanced skew rule. vendor_name, memo and line_amount values are on no allowlist, so they read [redacted] in every example.

- Gateway failures are value-free. They reach refresh.ts's per-domain catch as an A8GatewayReadError built from describeA8Error, so neither the FAILED log line nor the DomainResult can carry a row value.

- Freshness bounds replace the kit's 6h default (see the evidence below):

- 36h for transactions. mart-education-quickbooks-refresh is daily. Over the last 60 days its median gap was 24.0h and every gap but one was at most 34.8h, so 36h is the daily cadence plus 12h. Only the one missed-days gap (71.8h, 08-07 to 08-10) would have been refused.

- 8 days for vendors. quickbooks-expense-ai-generation is weekly (Sundays 08:00 UTC, 3-15 minutes), so 8 days is the cadence plus one day. It refuses the two windows where a failed Sunday run was recovered by hand 1-3 days late (10.3 days at worst); that surfaces the upstream outage instead of treating last week's classifications as current.

- Refusing is safe: each expense domain fails on its own, and nothing is deleted.

- Floors: 10,000 transactions and 500 vendors, about 12% and 14% of today's counts. There is no purge, so no purge guard. A short transactions read would still overwrite the one metadata row with shorter dropdown lists.

- refresh.ts changes stay inside interlude block 8: one import, four lines to resolve the gate once per cycle, and three changed calls.

- Dry-run: sync/src/scripts/dry-run-expenses-shadow.ts runs the worker's own shadow path, read-only.

## Business Value

- Moves the expense dashboard off the worker's direct Redshift reads. Its transactions, vendor classifications and filter metadata can now come through the governed Surtr Gateway. This is one of the A8 steps toward retiring the EC2 worker's warehouse access (AERIE-445).

- Low-risk cutover. Every expense write is an upsert, so nothing is purged. Each domain fails on its own. The metadata needs no new mart: one read serves both it and the transactions.

- Parity is provable before switching. The shadow compares every row and redacts personal free text. A test pins the SQL the Surtr marts copy.

- Zero risk until it is switched on. The default stays legacy, and rollback is one env var.

## Manual Effort Estimate

About 10 hours of focused work by hand, without AI. Keval, please confirm or adjust this number. It covers:

- reading the kit and the two Surtr mart contracts;

- the split and the gate module;

- the metadata derivation, including probing Redshift's DISTINCT and ORDER BY semantics;

- measuring both upstream cadences for the freshness bounds;

- about 45 tests with dual-transport fixtures;

- the read-only pg proof and the dry-run.

## Testing / evidence

- Split and gate parity on real rows (read-only). A throwaway script ran the pre-split expenses.ts (from d6f595810) and the post-split one against Redshift, reporting counts and booleans only. Everything matched on the first attempt.

| | rows | old = new | old = mapRows(sql) |

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

| transactions | 80,709 | yes, including watermarkMode/watermarks | yes |

| vendor classifications | 3,479 | yes | yes |

| metadata (68 schools, 2 accounts) | — | yes | — |

- Metadata derived from the 80,709 transaction rows equals the legacy metadata: school order is exact, account-name order is exact, and the account set is the same.

- The gate in all three modes was run over the same real rows. The "Gateway" was an in-process fake serving them in Data API shape plus lineage columns, so no Gateway traffic was generated.

- legacy, shadow and gateway each published exactly the old transactions, vendors and metadata.

- Shadow logged clean for all three sources: 80,709, 3,479 and 70 matched keys.

- Source shape (counts only): source_row_id is unique (80,709 of 80,709) and vendor_name is unique today (3,479 of 3,479). There are no trailing-blank or NULL-name edge cases in today's data.

- Redshift semantics probed read-only with literal-only queries, no table reads:

- VARCHAR DISTINCT keeps 'a' and 'a ' apart;

- ORDER BY is UTF-8 byte order ('' < '1' < 'B' < 'Z' < '_x' < 'a' < 'á' < '€' < '😀');

- NULLs sort last.

- Upstream cadence, from staging_other.pipeline_runs_prod over the last 60 days, read-only:

- mart-education-quickbooks-refresh: 68 successes, median gap 24.0h, gaps at most 34.8h except one of 71.8h.

- quickbooks-expense-ai-generation: weekly. Today's vendor snapshot is from 09-27 08:03.

- New unit tests:

- queries/expenses-gateway.test.ts (29 tests):

- fixtures in both transport shapes: NUMERIC as text, the ::text dates, '' versus NULL;

- fail-closed cases: NUMERIC delivered as a JSON number, an absent column, the legacy all-or-nothing parse, the freshness bounds at 35h/37h and 7.9/8.1 days, and the floors;

- metadata derivation: DISTINCT, NULL exclusion, byte order including a character where JS UTF-16 order differs, trailing blanks, and account ties;

- every mode and override: one transactions read shared by transactions and metadata; gateway failures that are value-free and never fall back; shadow degraded on Gateway failure;

- compare: field mismatches with vendor, memo and amount redacted, source_advanced, duplicate and one-sided keys, the vendor multiset, and the metadata sets.

- expenses-gate-refresh.test.ts (4 tests) runs a whole runRefreshCycle, using a stub orchestrator that runs the interlude:

- gateway mode publishes the Gateway snapshot and never reads the expense tables;

- a transactions-mart failure fails only transactions and metadata, with a value-free DomainResult error;

- shadow publishes exactly the pg reads and logs three clean lines;

- unset stays legacy.

- queries/expenses.test.ts (+4):

- pins the SQL text the Surtr marts copy;

- checks that each function equals mapRows applied to its exported SQL.

- Checks:

- sync: tsc --noEmit clean; vitest run --maxWorkers=2: 88 files, 1558 tests passed.

- pnpm lint (boundaries, convex paths, read bounds, test architecture, knowledge, biome) is clean. Its two warnings are pre-existing ones in chat/skill/forge-api/scripts/sindri.mjs.

- The chat suite wasn't run: this PR changes no chat code.

- Live Gateway dry-run: not run. The marts aren't deployed yet: AI-Builder-Team/Surtr#2088 is open and its DDL isn't applied. The aerie-a8 Gateway sources aren't registered or granted yet either (U04). Once they are, run cd sync && pnpm exec tsx src/scripts/dry-run-expenses-shadow.ts with the A8 key (Keval's step X5). With no key set, the script exits 1 and names the missing variable (checked).

## Stack

- Base: #1566 (the U02 read-gate kit, AERIE-2612). If #1566 merges first, this PR is rebased onto main and retargeted.

- AI-Builder-Team/Surtr#2088 (U07, SURTR-1539): the mart-aerie-expenses-refresh runner plus aerie_expense_transaction and aerie_expense_vendor_classification. The column names, types, lineage columns and mart_row_id here match those DDLs. The pinned SQL test here matches that PR's test_sql_contracts.py.

- No Convex change and no deploy-order constraint.

## Not covered

- The live Gateway dry-run and the G4 shadow window. Both wait for U07 to be deployed, U04 to be seeded and the key to be minted.

- A copy that lags its source reads as mismatch, not skipped. The kit's source_advanced rule covers a republish *between* the pg read and the Gateway read (the same rule as G1's directory). It doesn't cover the reverse: the upstream republishes and the A8 runner hasn't copied it yet. That window is normally a minute or two after each daily or weekly upstream run, so an hourly cycle rarely lands in it. If the runner is broken, the mismatch persists until the 36h / 8-day bound turns it into degraded.

- Transactions category freshness. It comes from the vendor classification joined at the mart's refresh, but the mart's lineage is the transactions run. If the runner fails on the weekly vendor trigger, categories lag until the next daily transactions republish re-joins them (at most about a day). The freshness bound can't see this; shadow shows it as category mismatches.

- No gateway-mode env precheck in the worker scheduler (the A5 pattern). A missing key fails each expense read with the client's value-free error.

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

#1623 — fix(admissions): return allowed portfolio statuses for invalid capacity filters @marcusdAIy  approved

## Summary

- AERIE-2341: remove the capacity-outlook portfolioStatus OpenAPI regex that rejected malformed filters before the existing handler could explain the allowed values.

- Retain a bounded string schema and the handler's status validation. No valid filter or capacity calculation changes.

- Add an authenticated HTTP regression test for unknown tokens, a trailing comma, and embedded whitespace; each receives a 400 with portfolioStatus and the allowed statuses.

## Validation

- Focused convex/publicApi/v2/admissions.test.ts invalid-filter test — passed.

- pnpm --dir chat typecheck — passed.

- Biome on changed files and git diff --check — passed.

- Pre-commit Convex paths, Biome, and chat typecheck — passed.

Closes AERIE-2341.

#1636 — fix(gateway): fail closed when a mapper drops a Gateway row (AERIE-2679) @kevalshahtrilogy  approved

Linear: AERIE-2679 (fixes a blocking Mercy finding on the release PR, against code from AERIE-2677; related AERIE-445)

## Summary

The finding. In ADMISSIONS_COMMUNITY_FUNNEL_READ=gateway, the legacy mapper drops a Gateway row with a null deal_id or one its schema rejects. Gateway mode only logged the count and then ran the population check on the reduced count. One dropped row in 5,403 is far inside the 5% allowance, so a partial snapshot would publish as a success.

The rule this PR applies to every Gateway read: if the mapper returns fewer records than the Gateway rows it was given, the read is refused.

- gateway mode: the read throws before the population check and before any write. The last published data stays. The message carries counts only, no row values.

- shadow mode: the same refusal makes the check degraded, never clean, so the shadow window shows it before gateway would refuse it. This matters because the mart is a verbatim copy: pg drops the same row, so the keyed compare alone would have called the cycle clean.

- Legacy (pg) paths are unchanged. Production publishes exactly what it publishes today.

What changed.

- Kit, sync/src/analytics/a8/gateway-table-reader.ts: assertNoA8RowsDropped(source, { rowsRead, recordsMapped }, reason). It throws the reader's own A8GatewayReadError, which is on the kit's value-free list, so the message reaches the logs as written.

- Community funnel, admissions-community-funnel-gateway.ts:

- readCommunityConversionFromGateway refuses any dropped row.

- Removed: the droppedRows and sourceRows fields (always 0 and N now), the warn-and-publish branch, and the second floor on the mapped count (dead once any drop throws).

- The module doc and the floor comment now describe the new behaviour.

- Expense vendor classifications, expenses-gateway.ts: the same refusal. It replaces the second floor on the mapped count, which only refused a read once it fell below 500 of about 3,479 rows.

- XO contractor identity, xo-contractor-identity-gateway.ts: a Gateway row whose aliases all trim away now fails the read. Before, it was filtered out and the rest published.

Every Gateway reader under sync/src, and what I found.

| Lane | Reader (Gateway source) | Can it drop a Gateway row? | Change |

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

| A1 | site-operational-metadata-gateway.ts | No. z.array(...).safeParse, then throw. Shadow only, never published. | None |

| A2 | schools-data-sheet-gateway.ts | No. Any invalid row, duplicate cell or partial grid refuses the snapshot; every record is published. | None |

| A3 | school-calendar-gateway.ts | No. Any invalid row or duplicate slug refuses the read; every row is sent. Convex skips a row whose site slug Aerie doesn't have, and the worker records that as an error on the run. | None |

| A4 | school-source-directories-gateway.ts (QuickBooks, SIS, Finalsite) | No. Strict parse; the snapshot builders map one to one and throw. | None |

| A5 | camps-full-gateway.ts (7 entities), camps-gateway.ts (parity checks) | No. Strict parse; loadCampSourceSnapshotViaGateway refuses the whole snapshot on a blank or duplicated id, mixed lineage or a dangling reference. | None |

| A6 | rebl3-sites-gateway.ts | No. Any invalid row, repeated site or mixed lineage refuses the snapshot. | None |

| A7 | matterport-discovery-shadow.ts (xref) | No. Strict; shadow only, never published. | None |

| F1 | xo-contractor-identity-gateway.ts | Yes: filtered out rows with no usable alias. | Fixed |

| F2 | xo-contractor-package-gateway.ts | No. Strict parse, one record per row. | None |

| A8 G1 | admissions-reference-gateway.ts (programs, Program directory) | No. mapProgramRows and mapHubspotProgramRows throw on any bad row. | None |

| A8 G2 | admissions-community-funnel-gateway.ts | Yes: null deal_id or schema-rejected. | Fixed |

| A8 G3 | community-deposits-gateway.ts | No. mapCommunityDepositRows maps every row or refuses the read. | None |

| A8 G4 | expenses-gateway.ts, transactions | No. Strict array parse; a conflicting repeated key is refused. An exact repeated row is published twice, as legacy does. | None |

| A8 G4 | expenses-gateway.ts, vendor classifications | Yes: safeParseRows skips a row it cannot parse. | Fixed |

| A8 G4 | expenses-gateway.ts, metadata | Not a row mapper. It derives two DISTINCT lists and leaves out NULLs exactly as the legacy SQL's WHERE ... IS NOT NULL does. | None |

## Business Value

- Unblocks the Aerie production release. Mercy will not approve the release while this finding is open on main.

- A snapshot that lost rows can no longer be published as a clean success on the admissions funnels, the expense vendor categories or the contractor identity index. Each of those would otherwise go quietly stale or short for the affected families, vendors or contractors.

- The shadow window now means what it says. A source that would be refused in gateway mode can no longer pass its shadow window clean.

## Manual Effort Estimate

About 4 hours of focused work by hand, without AI. Keval, please confirm or adjust this number. It covers:

- reading every Gateway reader and its publish path (13 reader files, plus the refresh code each one feeds) for drops;

- the kit helper and the three fixes;

- reworking the tests that pinned the old drop-and-publish behaviour, plus 18 net new tests.

## Testing

- a8/gateway-table-reader.test.ts (+4): equal counts pass; one dropped row of 5,403 is refused with the exact count-only message; the error carries its source; more records than rows is refused too.

- queries/admissions-community-funnel-gateway.test.ts (55 to 61):

- reader: one null-deal_id row and one schema-rejected row each refuse a 1,201-row read, with the exact message and no PII; 250 dropped rows are refused the same way; a clean read returns one record per row;

- gateway mode: one dropped row fails the read with A8GatewayReadError, the population preview is never called, nothing is logged as read, and pg is never called;

- shadow mode: pg and the Gateway both carry the same null-deal row. Legacy publishes as today, and the check is degraded, countsAsClean: false, with the count-only reason;

- end to end through refreshCommunityFunnel: a dropped Gateway row fails the refresh with no Convex call at all; legacy mode still publishes the pg read minus the row its mapper drops.

- Two tests that pinned the old behaviour (drop, warn, publish; floor after drops) were rewritten.

- queries/expenses-gateway.test.ts (31 to 34): the legacy mapper still drops a bad pg row; the Gateway reader refuses the same row; gateway mode fails only the vendors read with a value-free message while transactions still publish; shadow mode is degraded for vendors and clean for the other two sources.

- tests/redshift/xo-contractor-identity-gateway.test.ts (16 to 19): three shapes of an alias-less row each refuse the read with a count-only message and no name; blank entries between delimiters are still trimmed without refusing the row.

- tests/analytics/xo-contractor-identity-refresh.test.ts (+2): through the real Gateway reader. gateway mode returns an error and sends nothing; shadow mode publishes from pg and reports check failed.

- Checks:

- sync: tsc --noEmit clean; vitest run --maxWorkers=2: 113 files, 2515 tests passed.

- pnpm lint (boundaries, convex paths, read bounds, test architecture, knowledge, biome) is clean. Its two warnings are pre-existing ones in chat/skill/forge-api/scripts/sindri.mjs.

- The chat suite wasn't run: this PR changes no chat code.

## Does this refuse anything on today's data?

- Community funnel: no. The production mart was checked today (by Keval's session, not re-queried here): 5,403 rows, 0 null deal_id, 5,403 distinct.

- Vendor classifications: no, on the last evidence I have. The read-only check for the expenses gate on 2026-09-29 found 3,479 rows and none with a NULL name.

- XO contractor identity: not verified against production. The mart's SQL only emits aliases of 6 or more characters, so a row with none needs an all-whitespace contractor name. XO_CONTRACTOR_READ defaults to pg, and shadow mode would report such a row as a failed check before any flip.

- All three gates default to the legacy read, so nothing changes in production until a gate is set.

## Not covered

- The legacy mappers still drop rows on the pg path. That is deliberate (production behaviour is unchanged), but it means pg and the Gateway now differ on a bad row: pg publishes without it, the Gateway refuses.

- A persistent bad row in a mart keeps its shadow line degraded every cycle until Surtr fixes the row, since the Gateway read is refused before the compare runs.

- The held A8 PRs are not on main and were not audited: the per-program readers, the admissions pipeline gate, marketing and SIS enrollment. The pipeline refresh already counts droppedNoColumn and droppedNoProgram, so its gate needs this same rule when it lands.

- No live Gateway run. Gateway mode cannot be exercised end to end until the Surtr marts and the key grant are live.

- safeParseRows still logs a Zod message for the first three rows it skips. Unchanged here.

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

The Builder Desk  —  Engineer Spotlight
Production Release🏆 Engineer Spotlight

37 PRs, One Night: Builder Team Shatters the Sound Barrier Again

Kevalshahtrilogy alone outshipped most entire engineering orgs, dropping 18 PRs across Aerie while the rest of the roster refused to let him have all the fun.

Comrades, pull up a chair, because the scoreboard from the last 24 hours reads like a typo. Thirty-seven pull requests. Three repos lit up like a Christmas tree — Aerie absorbing a staggering 24 of them, Shipyard adding 7, Surtr chipping in 6. This is not a sprint. This is a team that does not understand the concept of resting.

The headline number belongs to @kevalshahtrilogy, who posted 18 PRs — HALF the team's entire output, single-handedly — spanning the A8 marketing seam work (#1587, #1586, #1578, #1576), Gateway shadow-compare fixes (#1637, #1633, #1634, #1632), and even a pricing doc for good measure (#2118). Eighteen. In one day. Somebody check this man's sleep schedule. Meanwhile @marcusdAIy quietly stacked six clarity-and-correctness PRs across Aerie's docs and admissions logic (#1626, #1625, #1624, #1622, #1621), proving that not every hero needs a cape — some just need a changelog. @caina-barbosa delivered three (#1629, #2117, and more), @sanketghia and @YibinLongTrilogy each notched a clean single (#2121, #1627) — no PR too small for the Numbers Desk.

And then there's @ashwanth1109. Eight PRs, including a full Shipyard release (#162) and the QuickBooks validation seam over in Surtr (#2123). The man ships code the way other people breathe — involuntarily, constantly, without apparent effort. 'I don't review my own diffs, I just trust the commit message,' Ashwanth allegedly told us this morning, which is either a joke or a terrifying confession, we genuinely cannot tell. Somewhere between #159 and #161 a reviewer reportedly aged visibly. When reached for comment on his output, Ashwanth said only: 'Next question,' and walked away.

The Overflow Desk is doing backbreaking work today. Mac's column had no room for #1587 and #1586, Kevalshahtrilogy's G2/G3 marketing seam double-feature, both load-bearing infrastructure dressed up as routine PRs. Also left on the floor: Ashwanth's Shipyard quartet of #159, #160, #161, and the release itself in #162 — a four-PR afternoon that would be somebody's entire month anywhere else. And don't sleep on #1627 from @YibinLongTrilogy, quietly adding dated backup site periods to DSS reporting while nobody was looking.

Morale report: through the roof. Unmeasurable. Off the charts and climbing. This is a team that treats a Tuesday like a championship round, and frankly, the rest of the industry should take notes.

Brick's Overflow — PRs Mac Didn't Cover  (click to expand)
#159 — AI-954: Show task creation time and time taken on task detail @ashwanth1109  no labels

## Demo

![AI-954 smoke test demo](https://github.com/AI-Builder-Team/Shipyard/blob/040a229e5e7e7568b55239d62958928ff28ac427/docs/smoke-tests/ai-954/demo.png?raw=true)

## Summary

- Adds a nullable Task.created_at (Unix seconds) via an idempotent migration. Existing rows stay null, with no backfill. Both task-creation paths set it to now().

- Task payloads now include createdAt, activeAgentSeconds, runningAgentTurns, and agentRuntimeMeasuredAt. Agent runtime is the sum of TaskTraceTurn durations. A turn that is still open counts up to now, or up to completed_at if the task is complete.

- Task detail header: the agent label row shows, right-aligned, a clock icon with the wall-clock duration and a bot icon with the agent runtime. Tooltips and accessible names give the full labels (Took 2h 14m or Running 45m, and Agent runtime 38m). For in-progress tasks these refresh every 30s, and the count includes turns that are still running.

- Task context sidebar: shows Created 3 days ago in a <time> element under the linked item/repository summary. Hovering shows the exact local date and time.

- Legacy tasks (createdAt null) show none of these values.

## Linear

https://linear.app/builder-team/issue/AI-954/show-task-creation-time-and-time-taken-on-task-detail

## Tests

- cargo test --lib: 340 passed. New tests cover the idempotent migration, legacy rows staying null, created_at on new tasks, and agent-runtime aggregation with open turns.

- node --test scripts/test-task-timing.mjs (new, pnpm test:task-timing): duration and relative-time formatting, completed/running/legacy summaries, and the UI hiding values for legacy tasks.

- node --test for task-workspace, workflow, task-trace, pr-review, and github-reviews: all pass.

- pnpm theme:check passes. tsc --noEmit reports only an existing, unrelated missing @tauri-apps/plugin-notification type in a worktree that has no local install.

#162 — Release: Shipyard 0.6.11 @ashwanth1109  no labels

## Summary

- Bump Shipyard to 0.6.11.

- Add the approved public release notes.

## Business Value

- Delivers the approved branch workflow, Pi conversation controls, repository history pagination, task timing visibility, and Pi image diagnostics.

## Implementation Effort

- Low: metadata-only release change; CI performs the signed build and publication.

## Test Plan

- [x] pnpm test:release

- [x] git diff --check

- [x] Verified the diff contains only package.json and releases/0.6.11.md.

#1586 — feat(a8): G2 per-program shadow compare + dry-run for ADMISSIONS_PROGRAM_DETAIL_READ (A8 U21) @kevalshahtrilogy  approved

Linear: AERIE-2625 (A8 unit U21; related AERIE-445, blocked by AERIE-2621)

## Summary

A8 group G2, per-program reads, part 3: the shadow compare for ADMISSIONS_PROGRAM_DETAIL_READ. shadow is now released for this gate. gateway stays behind the existing ADMISSIONS_PROGRAM_DETAIL_READ_NONPROD_OPT_IN convention.

- Shadow publishes exactly legacy (queries/admissions-program-shadow.ts).

- Each shadowed seam member runs the legacy query's SQL and binds once, through the legacy mapper, and returns (or throws) that result. It also keeps what it read.

- Nothing is read from the Gateway during the cycle.

- Checks run once the cycle has published. The orchestrator calls finishShadowChecks after the retention sweeps, before the non-admissions interlude. It never throws.

- Each shadowed mart is read once, through gateway mode's own row feed. createAdmissionsProgramGatewayRows is the Gateway path up to the mapper, extracted from U17's readers, so shadow checks exactly what gateway would publish.

- Results land as one a8_shadow_check line per source per cycle, via the kit's runA8ShadowCheck / logA8ShadowCheck.

- Compared per program, before any order-dependent step. programKey is the value the legacy predicate bound.

| Source | Records compared | Grouped by |

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

| Q5 pipeline students | mapped, without the #n recordKey suffix | recordKey base, as a multiset |

| Q6 enrollment cohort, Q7 deposits, Q8 transfers | the mapper's own parse, before pickBetterCohortRow | natural key, as a multiset |

| Q3 coming-year projections | the mapper's own parse, before first-row-per-year | year, code, version, as a multiset |

| Q4 community metrics, Q2 projections, app conversion | mapped | natural key (duplicates never clean) |

A failed call is recorded as a readFailed record: alone if its read or parse failed, beside the parsed rows if only the mapper refused them. Failing alike on both sides, on the same rows, matches; anything else does not.

- Derived outputs that publish are also compared, computed with the refresh's own functions from each side's mapped reads:

- derived:enrollment-snapshot;

- derived:pipeline-funnel (year-aware);

- derived:projection (enrollment and coming-year).

This proves the TS derivations get identical inputs. Programs whose inputs failed are skipped and counted.

- EduCRM skew rule. On a mismatch, only the calls whose records differ are re-read from pg, once, after the Gateway read. A match then is source_advanced, which counts as clean. Derived outputs recompute from those re-reads without further queries.

- PII. Values appear only for non-PII fields: program keys, cohorts, stages, school years and aggregate counts. Names, emails, ids and dates are redacted. Lines carry field names, value types, counts and lineage.

- Minimal core-file edits, no behaviour change:

- educrm.ts exports the four strict parsers its mappers already ran inline.

- enrollment.ts moves the cohort/deposit/transfer assembly into assembleEnrollmentCohortStudents (same order and errors) and exports deriveYearAwarePipelineFunnel.

- The cycle resolver moves to the shadow module, because it needs both transports.

- Dry-run: sync/src/scripts/dry-run-admissions-program-detail-shadow.ts. It loads dotenv first, then await import(). It runs every per-program read the refresh makes, for every program, plus app conversion, in shadow, then the checks. The refresh's Convex writes are not run.

## Business Value

- Starts the clean-shadow window for A8's longest chain (U03 → U08 → U14 → U17 → U21). That window is the gate before the worker's roughly 450 per-cycle EduCRM queries for these sources collapse into 8 Gateway reads (AERIE-445). The sources include enrollmentProjections, which feeds public API v2.

- Proves parity where it can actually break. Comparing before the order-dependent dedupes rules out false alarms from ties that legacy already leaves unspecified. The derived compares show the published snapshots, funnel and projections would be byte-identical.

- Zero publish risk. shadow publishes exactly the legacy reads (tested, and mutation-checked). The default stays legacy. gateway still refuses to start in production.

## Manual Effort Estimate

About 14 hours of focused work by hand, without AI. Keval, please confirm or adjust. It covers:

- reading the kit, U14's seam and U17's readers;

- deciding, per source, which step is order-dependent and what to compare before it;

- the per-call skew re-read and the derived-output compares;

- extracting the Gateway row feed without changing gateway mode;

- 13 new tests on a two-transport fixture;

- the dry-run;

- the read-only proof on real rows.

## Testing / evidence

- Read-only shadow proof on real rows. A throwaway script, not committed. default_transaction_read_only=on was set and checked. Output is counts and field names only: no row values, no program names.

- It runs this PR's shadow path end to end: resolveAdmissionsProgramSources in shadow, every per-program read for all 90 programs the worker refreshes, then app conversion, then finishShadowChecks.

- The Gateway side is a fake client whose marts are Surtr #2090/#2092/#2094's candidate SQL, run read-only against EduCRM on first read (after the pg reads, as in a real cycle). Values are reshaped as the Data API returns them (TIMESTAMP/DATE as text, BIGINT as numbers) and rows are reversed.

| line | outcome | pg / Gateway records | keys matched | re-read calls |

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

| aerie-admissions-pipeline-student (Q5) | clean | 33,585 / 33,585 | 33,585 | 0 of 90 |

| aerie-admissions-community-metric (Q4) | clean | 900 / 900 | 900 | 0 of 90 |

| aerie-admissions-enrollment-cohort (Q6, pre-dedupe) | clean | 9,238 / 9,238 | 9,238 | 0 of 90 |

| aerie-admissions-pipeline-deposit (Q7, pre-dedupe) | clean | 113 / 113 | 113 | 0 of 90 |

| aerie-admissions-enrollment-transfer (Q8, pre-dedupe) | clean | 36 / 36 | 36 | 0 of 90 |

| aerie-admissions-program-projection (Q2) | clean | 180 / 180 | 180 | 0 of 90 |

| aerie-admissions-coming-year-projection (Q3, pre-first-row) | clean | 180 / 180 | 180 | 0 of 90 |

| aerie-admissions-app-conversion | clean | 4,295 / 4,295 | 4,295 | 0 of 1 |

| derived:enrollment-snapshot | clean | 339 / 339 | 339 | 90 programs, 0 skipped |

| derived:pipeline-funnel | clean | 1,101 / 1,101 | 1,101 | 90 programs, 0 skipped |

| derived:projection | clean | 360 / 360 | 360 | 90 programs, 0 skipped |

There were 0 failed legacy reads, 8 mart reads (one per mart) and no mismatched, one-sided or duplicate keys. The pg reads took 290s, and the whole run 416s.

- New tests: queries/admissions-program-shadow.test.ts (15).

- The fixture is written once as pg rows and converted to Data API rows by each mart's column types. The real reader, parity layer and slicer then run.

- Publish invariance:

- each shadow member issues the legacy query once and returns or throws exactly its result;

- a whole cycle through refreshEnrollmentPipelineDetailed + refreshAppConversionFacts makes identical results, queries and Convex writes to legacy, while the Gateway serves different rows and one mart fails;

- the checks make no Convex write.

- One line per source: a matching cycle gives 8 source lines plus 3 derived lines, all clean. Each mart is read once and nothing is re-read. A per-source override shadows one source, and a derived output needs all its inputs.

- Compare granularity:

- Q5 order is ignored but a lost duplicate is not;

- dropping Q6's losing duplicate or Q3's second row per year is a mismatch even though the published output is identical.

- Skew rule:

- a republish between reads gives source_advanced for the source and its derived snapshot, re-reading only the differing programs;

- a pg read that failed during the cycle gives source_advanced;

- a Gateway-only difference stays mismatch.

- Failures and PII:

- a mapper failure on both sides matches only on the same rows, and its program is skipped for derived outputs;

- a check that throws outside the kit logs its own degraded line, and later checks still run;

- lines hold field names and non-PII keys, and no fixture PII value appears anywhere.

- Mutation-checked: comparing Q6 after dedupe, re-reading every call, or publishing from other rows each fails the suite.

- Updated tests:

- admissions-program-gateway.test.ts: shadow is released with no opt-in; gateway (alone or mixed with shadow) is still refused.

- refresh-orchestrator.test.ts (+1): the checks run once, with the cycle's programs, after the retention sweeps.

- Checks:

- sync tsc --noEmit clean;

- vitest run --maxWorkers=2: 90 files, 1,591 tests passed;

- pnpm lint clean apart from 2 existing warnings in chat/skill/forge-api.

The full chat suite was not run (no chat changes).

- Live Gateway dry-run: not run. The U12/U13 marts aren't deployed, and the slugs aren't registered or granted (U04). The dry-run script is ready for Keval's X5 step.

## Stack

- Base: #1578 (U17, AERIE-2621), which sits on #1576 (U14) and #1566 (kit). If #1578 merges first, this is rebased onto its base and retargeted.

- Mart contracts: Surtr #2090 (Q5/Q4), #2092 (Q6-Q8), #2094 (Q2/Q3/app conversion).

- Heads-up needed: this touches Vladimir's core files (queries/educrm.ts, queries/enrollment.ts, admissions/refresh-orchestrator.ts). The edits are extractions with no behaviour change. Keval to give Vladimir a heads-up before merge.

## Not covered

- gateway release: it stays behind the non-production opt-in until a clean shadow window. The plan asks for 48 cycles here, because enrollmentProjections feeds API v2.

- Published cohort-student rows and pipeline-student #n keys are not compared after dedupe. Legacy is itself nondeterministic on full ties there, so they're compared before it, per the plan.

- The Gateway-side mapper runs as the legacy one does, so its existing warnings (for example the Q6 duplicate-collapse line) print once per side in shadow.

- Q1 (pipeline_agg) isn't in the seam; U01 retires it.

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

#1587 — feat(a8): G3 marketing seam + Gateway readers D1–D4 + gate ADMISSIONS_MARKETING_READ, PII-redacted shadow compare (A8 U22) @kevalshahtrilogy  approved

Linear: AERIE-2626 (A8 unit U22; related AERIE-445)

## Summary

This is A8 group G3, marketing. It gates the four per-program EduCRM marketing reads behind ADMISSIONS_MARKETING_READ, using U14's seam pattern:

- D1 event aggregates;

- D2 event contacts;

- D3 shadow-day events;

- D4 weekly deposits.

It adds Gateway readers for all four, and a shadow compare that follows the EduCRM skew rule. The default is today's behaviour.

- Split, no behaviour change. Each of the four reads is now a SQL constant plus a mapper. The legacy query calls the mapper unchanged.

- SQL constants, byte-identical to the old text: MARKETING_EVENT_AGG_SQL, MARKETING_EVENT_CONTACTS_SQL, SHADOW_EVENT_AGG_SQL, DEPOSIT_AGG_SQL.

- Mappers: mapMarketingEventAggRows, mapMarketingEventContactRows, mapShadowEventAggRows, mapDepositAggRows.

- D1 and D2 keep their strict throws. D3 and D4 keep safeParseRows.

- Seam AdmissionsMarketingSources (queries/admissions-marketing-sources.ts). Each member has the legacy query's exact signature. PG_ADMISSIONS_MARKETING_SOURCES is the default everywhere.

- The core-file edits are threading only.

- admissions/per-program-refresh.ts: an optional marketingSources argument, passed to the four helpers. That is 4 call-site lines plus the argument.

- admissions/marketing-events-refresh.ts: one defaulted sources parameter per helper.

- admissions/refresh-orchestrator.ts: one injectable dep, resolved once per cycle. Its runShadowChecks() runs right after the per-program loop.

- The D1→D2 identity-manifest dependency is untouched: D2 still runs only after that program's D1 publishes.

- Gate ADMISSIONS_MARKETING_READ=legacy|shadow|gateway (default legacy), on the U02 kit. The overrides are …_MARKETING_EVENT, …_MARKETING_EVENT_CONTACT, …_SHADOW_DAY_EVENT and …_WEEKLY_DEPOSIT. If D1 and D2 would publish from different transports, the gate warns.

- Gateway mode (queries/admissions-marketing-gateway.ts). Sources are typed exactly as Surtr #2095 and #2099 store them.

- Each mart is read once per cycle and sliced by filter_program_name, with the exact legacy predicate.

- Each slice is re-sorted to the legacy ORDER BY before the unchanged mapper. Order decides what gets published:

- D2's (eventId, contactId) dedupe keeps the last row it sees;

- D4's Convex upsert is keyed by weekLabel, and 9,450 (program, week label) pairs collapse more than one SQL row today.

- SUPER strings sort by string value, as Redshift sorts them. This was checked on 146,830 rows: 0 order violations by value, 6 by JSON text.

- A failed read fails that source for every program this cycle.

- Floors are about a fifth of today's counts. The freshness bound is 24h, as for G2.

- Shadow mode publishes the legacy reads untouched and records each program's mapped output. After the loop it logs one a8_shadow_check line per source for the whole cycle, reading each mart once:

- D1 and D3 by (program, event name). Event names are free text, so they stay redacted.

- D2 as a multiset before the dedupe, with no allowlist: every value and key is [redacted]. The line carries field names, types and counts only.

- D4 at the full SQL grain (program, week label, week start, week end, school year), before the Convex collapse.

- EduCRM skew rule: on a mismatch, only the programs whose records differ are re-read from pg, once. If they now match, the result is source_advanced.

- A program whose legacy read failed is left out of the compare. If no program was read, the result is degraded, never clean.

- Dry-run sync/src/scripts/dry-run-admissions-marketing-shadow.ts. It loads dotenv first, then uses await import(). It runs the worker's shadow path for every listed program and exits 0 only if all four sources are clean.

## Business Value

- About 360 Redshift queries per cycle become 4 reads. Once cut over, 4 marketing queries × 90 programs collapse to one paged Gateway read per mart. That moves G3 off the worker's direct EduCRM access (AERIE-445).

- The cutover is provable and PII-safe. Shadow mode compares what would be published, before the order-dependent steps, and tolerates EduCRM's 30-minute republishes. The contact data carries children's dates of birth, and none of it reaches a log line.

- No behaviour change or prod risk now. The default path runs the same SQL text, the same mappers and the same Convex writes, as proved below.

## Manual Effort Estimate

About 14 hours of focused work by hand, without AI. Keval, please confirm or adjust. It covers:

- tracing the marketing refresh path and the D1/D2 manifest coupling;

- reading the U15 and U18 mart contracts;

- empirically checking Redshift's SUPER and ORDER BY semantics;

- the split, seam, gate, readers and cycle-level shadow compare;

- about 30 tests;

- the read-only proofs.

## Testing / evidence

- Read-only proof on real rows, for all 90 programs queryPrograms lists. The script was a throwaway and is not committed. It printed counts and hash prefixes only: no row values or program names. It compares three paths:

- OLD: this branch's base educrm.ts, a temporary copy.

- PG: the new split functions.

- GW: the new Gateway path. It was fed by the U15/U18 procedures' candidate SQL, run read-only against EduCRM through a raw-text pg client and reshaped into Data API values (BIGINT as numbers, DATE/TIMESTAMP as text, booleans), then passed through the kit's reader, the slicing, the re-sort and the mappers.

- The EduCRM run did not change during any source's reads.

| check | D1 events | D2 contacts | D3 shadow days | D4 deposits |

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

| mart SQL rows (NULL program dropped) | 1,041 (0) | 146,830 (0) | 75 (0) | 56,700 (0) |

| SQL text, OLD vs new constant | identical | identical | identical | identical |

| OLD = PG, per program | 90/90 | 90/90 as a multiset; 86/90 in order (see note) | 90/90 | 90/90 |

| OLD = GW, identical including order | 84/90 | 40/90 | 87/90 | 90/90 |

| the rest: same multiset, and the ORDER BY key sequence is identical, so they differ only among ties | 6 | 50 | 3 | 0 |

| what publishes (D2 after its dedupe; D4 after the Convex upsert by weekLabel) | n/a | 90/90 identical | n/a | 90/90 identical |

| whole-cycle shadow check (GW fed as above) | clean, 866 keys | clean, 63,931 records / 30,118 keys (22,648 repeated pairs, compared as a multiset) | clean, 73 | clean, 56,700 |

- Note on D2's 86/90. Redshift returns rows that tie on D2's ORDER BY event_id, funnel_state_code, last_name in a different order from run to run. So two back-to-back runs of the identical SQL can differ in order only, which is why the multiset result is 90/90. The ties never decide what publishes: 0 (event, contact) groups have a dedupe winner that ties on the full ORDER BY with a differing row.

- SUPER ordering, checked separately. All 146,830 D2 source rows were ordered by Redshift. Comparing adjacent rows with the reader's comparator found 0 violations. Comparing them as JSON text found 6.

- New tests (about 30):

- queries/admissions-marketing-gateway.test.ts (21):

- gate modes and overrides;

- the mixed D1/D2 transport warning;

- read-once, and exact slicing, including a trailing blank and a NULL key;

- D2 type parity against the pg shape;

- D2's strict throw kept;

- D1 and D4 legacy order;

- the D2 SUPER and D4 NULL-ordering comparators;

- failure fan-out without a retry.

- Shadow tests in the same file:

- publish-invariance: it returns the legacy object untouched and reads no Gateway in the loop;

- D3 clean by (program, event);

- the skew rule re-reads only the differing program, giving source_advanced;

- a persistent mismatch;

- failed legacy reads give degraded;

- D2 multiset with full redaction: no email, program name or id in the result;

- D4 at full grain with allowlisted examples.

- queries/educrm.test.ts (+4): each split read runs its constant with [programName] and returns its mapper's output.

- admissions/marketing-events-refresh.test.ts (+2), per-program-refresh.test.ts (+1), refresh-orchestrator.test.ts (+1): the sources are threaded, and the shadow checks run right after the loop and change no result.

- Checks:

- sync tsc --noEmit is clean.

- vitest run --maxWorkers=2: 89 files, 1,591 tests passed.

- pnpm lint (biome) is clean.

- The full chat suite was not run, because nothing in chat changed.

- Live Gateway dry-run: not run. U15/U18's marts aren't deployed, and the aerie-a8 sources aren't registered or granted.

## Stack

- Base: #1576 (U14 seam, AERIE-2619), which sits on #1566 (U02 kit). If those merge first, this is rebased and retargeted.

- AI-Builder-Team/Surtr#2095 (U15, D1/D2 marts) and AI-Builder-Team/Surtr#2099 (U18, D3/D4 marts), both open. Column names, types, lifted keys and lineage match their DDL, and their candidate SQL fed the evidence above.

- Touches admissions/per-program-refresh.ts, where collision is MEDIUM (U21 and Vladimir), with threading-only edits.

## Not covered

- A live Gateway run. The marts aren't deployed, and the aerie-a8 sources aren't registered or granted (U04, key).

- Mart refresh lag. The skew rule re-reads pg, so it covers pg being *older* than the mart. If EduCRM republishes mid-loop and the mart hasn't refreshed yet when the check runs, the programs read after the republish can show a mismatch. The line carries the mart's source_run_id and source_published_at to diagnose this. Judge the shadow window on a run of cycles, not a single line.

- D2 is the largest read, about 147K rows. In gateway and shadow modes the worker holds one D2 snapshot, plus the recorded per-program outputs in shadow mode, for the cycle.

- Out of scope here:

- D5 DOP weekly (U01 retires it);

- the AERIE-2353 behaviour, which is kept for parity;

- the G2 per-program shadow (U21);

- no Convex changes.

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

#1627 — Add dated backup site need periods and DSS reporting @YibinLongTrilogy  approved

## Summary

Add dated backup site need periods for each Site. Each period records need dates, progress (Requested, Searching, Signing Contract, or Acquired), and the selected backup location and contract term when acquired. Dated DSS questions use these periods; the existing operating confirmation continues to answer where a Site is actually operating now.

## Screenshots

<img width="1288" height="883" alt="Screenshot 2026-10-01 at 4 21 03 PM" src="https://github.com/user-attachments/assets/81331a05-224e-492b-b2e4-7188828ebc3f" />

## Changes

- Add the need period schema, validation, Convex mutations, read model, and shared contract. Need periods can have gaps or an open end, but cannot overlap for one Site.

- Add period editing and status controls to the Site backup card, with guidance when today's plan and the confirmed operating location differ. Clarify the operating control in Backup Sites administration.

- Include need periods in the existing per-Site public API, agent, and Rhodes MCP read paths, and update the DSS instructions for dated questions and operating handoffs.

- Add focused tests for the period rules, UI, public API, agent, and Rhodes reads.

## Design decisions

- Need dates describe planned coverage; contract dates describe a location's agreement; the operating checkbox confirms actual current operation. These remain distinct so a planned handoff does not silently change the operating answer.

- Existing backup records continue to work without a backfill. This PR does not add an API write endpoint or a portfolio-wide aggregate.

## Business value

Teams can see and report which upcoming stretches have secured backup space and which still need work, including a return to an earlier location.

## Estimated manual effort

Approximately 3–4 focused engineering days without AI.

## Test plan

- [x] Focused Convex tests: 199 passed across four files.

- [x] Shared contract tests: 4 passed.

- [x] Rhodes worker backup site tests: 9 passed.

- [x] UI and public API tests passed during implementation.

- [x] Chat typecheck and pre-commit checks passed.

- [ ] Manual: add multiple periods with a gap and an open end; verify overlap rejection, acquired location and term selection, operating handoff guidance, and dated DSS answers.

#2123 — Enable on-demand QuickBooks Core validation (SURTR-1593) @ashwanth1109  approved

## Business Value

Enable financial mapping repairs to rebuild from an accepted immutable QuickBooks snapshot without repeating the roughly 45-minute extraction of 68 companies. Live validation measured Core at 4m 9s and the successful financial report stage at 2m 26s. Today's full elapsed validation was 23m 11s because an unrelated scheduled HubSpot admissions transaction blocked competing financial writers and the first report attempt timed out.

## Change

Enable quickbooks-core-tables.on_demand_enabled and remove only Core from the application query layer's matching legacy hold. Explicit production registry metadata already enables the live route. Empty runner parameters, the disabled schedule, raw-success subscriptions, run records, runner/image, IAM, timeouts and financial contract checks are preserved.

The candidate was deployed before merge only to the isolated Core production stack and its required registry stack, each exclusively with [1/1]; both reached UPDATE_COMPLETE. Registry parity confirms the other 170 pipelines' controls are unchanged. No other pipeline stack was deployed.

## Validation

- All required CI checks, all 15 existing on-demand control tests, scoped Biome and whitespace checks pass at c0ca0a073ecbf50a11e26b955c7b29373d6bcd2c.

- Supported Core ON_DEMAND execution core-fast-financial-validation-20261002-105212 succeeded, using {} parameters and accepted raw run 6cad5602-4634-4681-9b31-d926b29b33d4; new Core generation is fe6f4160-39f5-4116-b2cd-4b9b052a9e65:posting.

- Core success automatically triggered the financial mart. That first mart confirmed cancellation after a 520-second lock wait. After HubSpot and both queued School Performance writers succeeded and fresh source/lineage/lock preflight passed, report-only ON_DEMAND retry financial-validation-lock-retry-20261002-111257 succeeded; no extra Core or raw run was needed.

- All 24 Core audits pass. Coordinated reconciliation preserves all 539 posting IDs, dates, signed amounts and categories; verifies nine exact School assignments and 30 existing School scopes; confirms 59 Schools, complete QTD shapes, zero GL/lineage/revenue/Timeback mismatches, $270,595.89 dedicated-book QTD Facilities expenses and the separate −$9,900 workshop credit. This validation includes separately applied Finance policies and Facilities/Guide procedure fixes; they are not changes in this configuration PR.

Future validation must wait for relevant financial and shared-School writers to be idle. The successful-stage total is not a promise of contention-free end-to-end timing.

## Implementation Effort

Approximately 2–3 hours for an engineer to inspect controls, deploy the isolated configuration/registry changes and perform live validation; source coordination and pipeline waiting time are additional.

## Linear

[SURTR-1593](https://linear.app/builder-team/issue/SURTR-1593/enable-on-demand-quickbooks-core-validation-from-accepted-snapshots), a sub-issue of Finance escalation SURTR-1496.

The Portfolio  —  Trilogy Companies

Skyvera Goes on a Shopping Spree — CloudSense, STL Assets Land in Austin's Telecom Trophy Case

Word on the wire: Skyvera's buying binge adds a Salesforce-native CPQ darling and a BSS grab-bag, and it's doing it all with AI speed that'd make legacy telecom vendors blush.

AUSTIN, TEXAS — The deal desk at Skyvera's been burning the midnight oil, kids, and the ink is finally dry. Skyvera has completed its acquisition of CloudSense, the Salesforce-native configure-price-quote darling that's been turning heads in telco back-offices from London to Lagos. This scribe hears the whispers started months back, but now it's official — CloudSense slides into the Skyvera family alongside Kandy, VoltDelta, ResponseTek, and the rest of that telecom modernization crew.

And it's not just paper-pushing, honey. CloudSense arrives with receipts. The outfit just certified all 13 of its CPQ APIs to TM Forum compliance standards — a slog that normally eats 26 months of engineering calendar — in a tidy 30 days flat. How? A strategic AI partnership did the heavy lifting traditionally reserved for armies of integration consultants. Twenty-six months to one. Let that sink in, Telecom City.

This fits the Skyvera house style to the letter — AI isn't a garnish here, it's the whole recipe. CloudSense bills itself as the only AI-powered CPQ built for telco's messiest revenue segments: B2B, B2B2X, wholesale — the stuff that makes enterprise sales reps weep. Built atop Salesforce's billion-dollar AI investment, no less.

But Skyvera wasn't done. A little bird in the digital BSS world confirms the company also scooped up STL's telecom products group — monetization, optical networking, analytics, the whole divested bundle — tucking it quietly into the portfolio alongside the CloudSense splash.

Sources close to the deal say Skyvera's playing this smart — bolting on complementary telecom tech while squeezing it through the same AI-accelerated integration engine that's becoming Trilogy's calling card. Totogi, watch your back. The telecom modernization race just got another runner, and it's wearing Skyvera's colors.

↗ TelcoDR’s Skyvera snaps up CloudSense - telecomtv.com  ·  Cloudsense  ·  CloudSense achieves TM Forum API compliance in record time u

As Microschools Multiply, Regulators Scramble to Catch Up — and Alpha Keeps Scaling Anyway

A grassroots education movement is outpacing the rulebooks meant to govern it, and Joe Liemandt's AI-driven school model sits squarely at the center of the storm.

AUSTIN, TEXAS — There is a particular kind of American story unfolding right now in statehouses and strip-mall classrooms alike, and it goes something like this: parents, frustrated and improvising, are building something new faster than the people whose job it is to regulate it can figure out what it even is.

That's the throughline in a recent wave of reporting — from a one-room schoolhouse in Campbell County, Wyoming, quietly swelling with new students, to a Stateline dispatch documenting how microschools are proliferating faster than state licensing frameworks can absorb them. The numbers, anecdotal as they still are, point to the same conclusion: families are opting out of the traditional seat-time model in numbers that should worry — or at minimum interest — anyone who still believes the factory-era school day is sacred.

It would be journalistic malpractice not to note that Austin's own Alpha School, Joe Liemandt's two-hour-a-day, AI-tutored academic model, has effectively been running this experiment at scale for years, and is now positioning Timeback as the infrastructure layer for exactly this moment — the "Shopify for schools" pitch aimed squarely at the entrepreneurial parents and ex-teachers opening microschools in strip malls and church basements nationwide.

The systemic question underneath all of it is one of accountability without uniformity. Microschools, almost by definition, resist the kind of standardized oversight that traditional districts accept as the price of public funding. That's a feature for the libertarian-minded parent who wants curriculum control; it's a potential liability for the state official tasked with ensuring a child somewhere isn't simply falling through the cracks with better branding.

Alpha's own answer — publish the test scores, let NWEA MAP Growth data do the talking — is a bet that transparency of outcomes can substitute for uniformity of process. Whether regulators in Wyoming, or anywhere else drafting rules in real time, will accept that bargain is the open question this movement still has to answer. For now, the paperwork is losing the race to the parents.

↗ 5 Trends Reshaping K-12 Education Across the U.S. - The 74 M  ·  Microschools are growing in popularity, but state regulation  ·  Micro-Schools: The Education Trend That Is Here to Stay - Bo

Sixty-Six Days to Close: ESW's Marin Software Deal, and the Blog Posts That Ran Alongside It

While the acquisition machine closed another distressed software company in record time, Trilogy's education arm was busy telling parents how to nurture their children's 'creative genius.'

SAN FRANCISCO — Marin Software spent the better part of two decades as a Nasdaq-listed ad-tech also-ran, a company that once commanded a market cap north of a billion dollars and, by the end, could not. Last week, ESW Capital closed its acquisition of the company in what court filings and trade press are calling a 66-day case — fast, even by the standards of an outfit that has made speed and cheapness its signature.

The arithmetic is familiar to anyone who has followed ESW's 75-plus acquisitions since 2006. Buy distressed or underpriced enterprise software at 1–2x revenue. Replace expensive local staff with Crossover's global talent pool. Push support pricing up on customers who are too locked in to leave. Target the 75% EBITDA margin that ESW considers, in its own words, a moral signal of efficiency rather than a product of what got cut to get there. Marin's advertisers and agency clients — the ones who kept paying for campaign management tools through a decade of shrinking revenue — are about to find out what that margin extraction looks like from the inside.

Sixty-six days is fast enough that most of Marin's rank-and-file employees likely learned about their new ownership structure around the same time the deal closed. It is worth asking who had sixty-six days of notice, and who did not.

Meanwhile, on the other side of the Trilogy ledger, Alpha School — the K-12 venture where founder Joe Liemandt serves as principal — published the latest installments of its parenting blog series, including one on unleashing your kid's creative genius at home and another addressing the recurring question of whether AI is replacing teachers (the answer, per Alpha, is no — humans handle motivation and relationships, AI handles delivery).

Two arms of the same holding company, two very different audiences being asked to trust the operating system. One is told AI will give their child back two hours of childhood. The other is finding out, in real time, what AI-enabled efficiency does to the price of software they already depend on. Both messages originate from Austin. Only one of them comes with a 66-day clock.

↗ Teach Your Kid What School Doesn’t (Pt. 5): Unleashing Their  ·  Does Alpha School Replace Teachers with AI?  ·  Teach Your Kid What School Doesn’t (Pt. 4): How to Regulate
The Machine  —  AI & Technology

The Liability Vacuum: AI Firms Spend Billions, Dodge Blame

As courts struggle to assign fault for rogue algorithms, the industry's own financiers are growing skittish about the politics of unaccountability.

SAN FRANCISCO — Two numbers frame the contradiction at the center of the artificial intelligence boom: $10 billion and zero.

The first is the fresh valuation assigned to Instinct, an AI agent startup that this week raised $1 billion, according to Bloomberg. The second is the count of major U.S. court rulings that have clearly established who pays when an autonomous AI agent causes real-world harm.

Legal scholars examining the question have found no clean answer. Product liability law, built for toasters and automobiles with fixed failure modes, strains against software that updates itself and makes decisions its own engineers cannot fully trace back to a cause. Some scholars argue existing tort frameworks can stretch to cover "runaway" AI; others say Congress will eventually have to write new statute, much as it did for aviation and nuclear power in the mid-20th century, as a recent review of the legal landscape lays out. Until then, liability remains a question mark hanging over a trillion-dollar industry.

That ambiguity is becoming a political liability of a different sort. Greg Brockman, OpenAI's president and co-founder, has told colleagues he will not make a second $25 million contribution to Leading the Future, the AI-industry super PAC, calling it a "distraction," according to people familiar with his thinking. The PAC was built to blunt state-level AI regulation; Brockman's retreat suggests at least one major lab is recalculating the cost of looking too eager to buy its way out of accountability, even as rivals keep writing checks.

The pattern echoes a broader industry habit of socializing risk while privatizing upside — visible too in Meta's use of AI data center spending to claim research tax credits its own accountants have flagged as aggressive. Regulators, courts and voters are still deciding who holds the bag. The capital, meanwhile, keeps moving faster than the law.

↗ Inside Binance Founder Changpeng Zhao’s Life After Prison  ·  Who’s to Blame When A.I. Goes Rogue?  ·  OpenAI’s Greg Brockman Backs Out of Second $25 Million Donat

The Personality Test We Never Thought to Give a Tumor

A century-old tool for measuring human aptitude has been turned on cancer itself, revealing which malignancies are the stubborn geniuses of molecular evasion.

LONDON — In 1952, a Danish mathematician named Georg Rasch wanted to know something deceptively simple: given a set of test questions of varying difficulty, how do you fairly rank a set of students of varying ability? His answer, Item Response Theory, became the quiet architecture behind the SAT, the GRE, and nearly every standardized exam you have ever resented. It assumes two things exist in tension — an item's difficulty and a subject's ability — and it solves for both simultaneously, even when the data is incomplete, even when not every student answers every question.

Seventy-three years later, researchers have pointed this same instrument at something that has never sat for an exam: cancer.

In a paper released this week, scientists applied what they call reverse Item Response Theory to 242,036 drug-sensitivity measurements from the Genomics of Drug Sensitivity in Cancer database — a sprawling, maddeningly sparse ledger of which tumor types resist which compounds, and by how much. The inversion is almost poetic: cancer types become the "subjects" being tested, possessing a latent resistance ability; drugs become the "items," each with its own evasion difficulty. A pancreatic tumor that shrugs off a dozen different compounds isn't lucky — it is, in the statistical sense, smart. It is acing the test.

The elegance here is that IRT was built for exactly this kind of wreckage: not every cancer line was tested against every drug, just as not every student answers every question. Where a simple average would be fooled by missing data, the latent-trait model keeps ranking cleanly, assigning both drugs and malignancies a stable position in a hidden space of difficulty and resistance.

It's a reminder of something the deep-learning world is relearning on its own terms this month — a separate paper asks how far the Adam optimizer really sits from natural gradient descent, another case of a familiar tool turning out to encode a geometry we hadn't fully seen. Measurement, it seems, is never just measurement. It's a map of structure we didn't know we were standing on.

↗ Reverse Item Response Theory for Sparsity-Robust Ranking in  ·  How Far is Adam from Natural Gradient Descent?  ·  FourierQK: Filter Shape, Admissibility and the Leakage-Cover

The Great Capacity Glut: A Predator Turns Prey-Seller in the Cloud Savanna

Having gorged itself on silicon beyond any creature's natural need, the social media giant now learns to peddle its leftovers — as the wider hyperscaler herd swells toward a trillion-dollar watering hole.

MENLO PARK, CALIFORNIA — Observe, if you will, the curious behavior of Meta, a beast long content to hoard its vast stores of computing power for its own private rituals of recommendation and reels. Here, in the humid data-center basin of Northern California, something remarkable has begun to stir.

According to reports first surfaced by Bloomberg, Meta has begun constructing a cloud computing business of its own — not out of fresh ambition, but necessity. The creature, it seems, overbuilt. Years spent amassing GPU clusters for the training of ever-hungrier AI models have left it with surplus capacity sitting idle, like a lion that has killed too much and must now barter the carcass before it spoils.

This is no small adaptation. Wall Street analysts, watching closely from the ridge, warn via CNBC that this pivot into infrastructure-selling will mean thinner margins ahead — the price of diversification in a hostile ecosystem long dominated by three established apex predators: Amazon, Microsoft, and Google.

And what an ecosystem it is. The broader hyperscaler biome, fed by the relentless convergence of artificial intelligence and digital assets, is now projected to swell past one trillion dollars in revenue by 2030, per a new forecast circulated by ET CIO. The watering hole, in other words, is large enough yet for even a late arrival to drink.

One wonders whether Meta's entry signals not confidence but overextension — a species so eager to prove its dominance in the AI arms race that it built more den than it could ever occupy alone. Nature, ever efficient, abhors waste. So too, apparently, does the modern balance sheet. The surplus must be sold, the margins must thin, and the herd — ever larger, ever hungrier — presses on toward the horizon of 2030.

↗ Hyperscaler cloud revenues projected to top USD 1 trillion b  ·  Meta’s push into cloud computing means Wall Street has to pr  ·  Meta building cloud business to sell excess AI capacity, Blo
The Editorial

Desperate Companies Discover That Buying A Fleet Of Robotaxis Is Cheaper Than Having A Business Model

Why bolt on an actual revenue stream when you can just point at some cars that drive themselves and hope nobody asks follow-up questions?

SAN FRANCISCO — For a brief and shining moment, the zombie company — a firm with no profits, no plan, and a stock price sustained entirely by vibes — needed Bitcoin to stay interesting. Buy enough crypto, put it on the balance sheet, call it a "treasury strategy," and suddenly a company that sells, say, struggling regional mattresses could be described by analysts as "forward-looking." But Bitcoin is old news now, roughly nine internet years old, and nothing dies faster in corporate America than a trend that's merely successful. The new move, as Electrek reports, is Tesla Robotaxis — specifically, buying a fleet of them you have no regulatory approval, operational plan, or honest intention of ever running, purely so your quarterly filings can contain the phrase "autonomous mobility assets."

Industry observers describe the appeal as self-evident. Unlike Bitcoin, which merely sits there appreciating or depreciating in embarrassing silence, a parked Robotaxi fleet can be photographed. Shareholders, it turns out, respond well to a picture of forty white Model Ys gleaming in a lot somewhere outside Fresno, even if every single one of them is, legally and literally, going nowhere.

This is of a piece with the broader corporate mood heading into 2026, according to a CFO.com roundup of 13 essential buzzwords every finance chief should master before their next earnings call. Terms like "agentic liquidity" and "synthetic headcount" now function less as descriptions of anything real and more as a kind of incantation — say them with enough confidence in a boardroom and the board forgets to ask what the company actually does. One CFO reportedly deployed four of the thirteen buzzwords in a single sentence and was promoted before security could verify he'd finished the sentence.

Skeptics note that companies hyping AI are simply replaying the sustainability playbook from a decade ago — big pledges, vague metrics, a press release with a tree on it — except this time the tree is a robot and nobody has to plant it. The honest version of most 2026 shareholder letters would read: "We do not have a product. We do, however, have cars." Analysts expect this to be received as visionary.

As for the Robotaxis themselves, sitting in their lots, batteries trickle-charging, destined never to pick up a single human passenger — they remain, by every available financial metric, the single most productive asset many of these companies own.

↗ Tesla Robotaxi fleets are the new crypto treasury for zombie  ·  Train First. Announce Second. Why Your Brokerage AI Rollout  ·  13 buzzwords CFOs should know for H2 2026 - CFO.com
The Office Comic  ·  Art Desk
The Office Comic  ·  Art Desk

Pursuant to Forthcoming Regulatory Review: An Analysis of 2026 Antitrust Posture as It May (or May Not) Pertain to the Aforementioned Enterprise Software Roll-Up Strategy

Notwithstanding the absence of any direct enforcement action against the undersigned's employer's parent conglomerate, multiple industry analyses suggest 2026 may bring heightened scrutiny of acquihire transactions and serial acquisition strategies of the sort practiced broadly across the technology sector.

WASHINGGTON — It is hereby noted, for the benefit of the reading public and notwithstanding the inherently speculative nature of regulatory forecasting, that several prominent legal commentaries have, as of the date of this writing, converged upon a singular question: whether the enforcement posture heretofore characterized by certain commentators as "America First" antitrust policy shall, in the fiscal year 2026, continue substantially unchanged, or whether material deviation therefrom should reasonably be anticipated.

Per the analysis set forth in Wilson Sonsini's Big Tech year-in-preview, it is contemplated that regulators shall maintain, if not intensify, their examination of acquisition structures heretofore utilized throughout the technology sector generally, including without limitation those transactions colloquially referred to as "acquihires." The undersigned notes, for purposes of full disclosure and in the interest of avoiding any inference of self-interest, that no portion of this article should be construed as an admission, acknowledgment, or concession regarding the applicability of said scrutiny to any particular enterprise software acquisition vehicle, including but not limited to those which acquire companies at valuations of one to two times annual recurring revenue, a practice which is, as a general matter, widespread industry-wide.

Separately, and pursuant to guidance issued by WilmerHale concerning the Federal Trade Commission's heightened interest in talent-centric transaction structures, it is observed that the agency's theory of harm — namely, that the acquisition of key personnel absent corresponding asset transfer may nonetheless trigger reportability obligations under the Hart-Scott-Rodino framework — remains, as of present publication, unresolved by binding precedent.

The reader is cautioned that the foregoing summary constitutes general commentary only, is not intended as, and shall not be construed as, legal advice applicable to any specific transaction, structure, or entity, and that persons seeking guidance as to the applicability of the aforementioned enforcement trends to their particular circumstances are advised to consult counsel accordingly.

↗ Looking Ahead on US Antitrust Enforcement and Tech: Will 202  ·  2026 Antitrust Year in Preview: Big Tech - Wilson Sonsini  ·  2026 Antitrust Outlook: Learnings From the First Year of ‘Am
⬛ Daily Word — Artificial Intelligence
Hint: A unit of text that an AI model processes or generates.
Share this edition: 𝕏 Twitter/X 🔗 Copy Link ▦ RSS Feed