# 24.x → 25.0.0, entry by entry

**Forty entries** — `CHANGELOG.md`'s `25.0.0` breaking entries in their order: Bun
first, then grouped by package, lowest tier first. No legacy path, no codemod, no compatibility
shim — every break is a build error or an `X_*` error naming the rewrite. Most of the major is
deletion: a second spelling, a second projection or a second page shape removed, and the keys and
re-exports a deprecation promised to remove. **Entries 2 and 8 refuse the boot** of an app that
still writes a deleted or an undeclared key. **Entry 12 is silent:** a per-action bucket in `configureHttp` stops
limiting anything, so move it before the deploy — upgrade step 9. **Entries 22–24 refuse every AI
call** until the app registers its own models and names one — upgrade step 8. Entries 31–40 landed
after the cut, so they sit below 30 rather than with their packages. **Entry 38 is silent:** a worker
Deployment that set `WORKER_QUEUES` stops claiming every queue it does not list — upgrade step 12. A later slice
appends its rows below the last one and never renumbers; each row's `CHANGELOG.md` line names it
as `(#N)` after the dash.

### The upgrade, top to bottom

| # | Do | What you see until you do | Entries |
|---|---|---|---|
| 1 | `bun upgrade` to 1.4.2 and move the app's own image to a 1.4.2 base | `X_BUN_VERSION` from `x` and `x doctor` | 1 |
| 2 | pin every `@ultimat3/*` to the one new version, `bun install` | nothing yet — a mixed install is untested | — |
| 3 | delete the removed keys from `app.config.ts` and each overlay — the seven of 25.0.0, the ten of earlier majors, and any key the refusal says is not one (fix the typo it suggests); declare the locales in `defineCatalogs` (`packages/i18n/src/index.ts`); move a non-`/mcp` `ai.mcp.path` to the first `defineAppMcp({ path })` in `apps/<app>/mcp.ts`; move `drainTimeoutMs` to `drain: { deadlineMs }` and delete `buildIdHeader` from `configureHttp` | TS2353 in a typed config; `X_CONFIG_INVALID` naming the key and its replacement, or the nearest real key, at the first import | 2, 8 |
| 4 | `bun run typecheck` for the renames and removed exports: factory spellings (every `create<Thing>` and `new` of a class now a type), every value a second package re-exported, the renamed value names, `checkEnvExample`, `fingerprint`, `nearestName`, `promptHash`, `entity`'s `Page`, `SQL_*`, `createNatsDriver` and `createRedisDriver` imports | TS2305 / TS2724 at each import; TS1485 / TS1362 at a class now exported as a type | 3, 5–7, 14, 15, 20, 28, 31, 32 |
| 5 | `bun run typecheck` for the MCP projection, the MCP block and the page shape: `.tool()` and `toolFromAction` → `toolFrom`, `QueryMcp` / `McpExposure` / `McpAnnotationHints` → core's `McpExposureDeclaration` family, `mcp.name` → the primitive's `name`, `endCursor` / `hasNextPage` → `nextCursor` / `hasMore`, a storage page's `objects` / `truncated` / `cursor` → `rows` / `hasMore` / `nextCursor` | TS2339 / TS2305 / TS2353 at each site | 7, 10, 11, 18 |
| 6 | `bun run typecheck` for the members a custom implementation owes: `AuditRecord.name` / `.primitive`, `IdempotencyStore.keepsRedaction` and `settle(…, redacted)`, `WebhookLedger.isDisabled`, `ChangeEvent.write`, `ChangeEvent.table` for `.entity`, `DigestAppend.appender`, a `StorageDriver.list()` that answers `Page<StorageListEntry>`; drop `counted` from every `ack`; widen a `Menu` trigger's `aria-controls`; read `listenerFor` instead of `listeners`; narrow a read of `config.jobs.concurrency` with `typeof … === 'number'`; pass an `OAuthGrantContext` where code calls a `ResolveOAuthGrants` | TS2741 / TS2554 / TS2322 / TS2339 / TS2353 at each site | 4, 7, 13, 16, 17, 21, 25, 26, 33, 34, 37, 40 |
| 7 | `bun run typecheck` for ai: pass `models` to each `anthropicProvider(…)`, replace `OPENAI_MODEL_IDS` with the app's ids | TS2554 / TS2305 at each site | 23 |
| 8 | **before the deploy, where the app calls a model:** `registerModel` every model it names (by convention in its own `models.ts`), and name one on each declaration or as `providerGateway({ defaultModel })`; a test registers its rows in `beforeEach` and passes `model` to `echoProvider()`'s requests | `X_AI_MODEL_UNKNOWN` before any provider call; `X_AI_MODEL_UNRESOLVED` at a call that names none | 22–24 |
| 9 | **before the deploy:** move each `rateLimit.buckets.<actionName>` from `configureHttp` to that action's `rateLimit: { limit, windowMs }` | `X_CONFIG_INVALID` at boot, naming the bucket and the action | 12 |
| 10 | `x env example` (an app exporting `envSchema`), `x manifest`, commit `.env.example`, `x.manifest.json` and `openapi.json` | `X_ENV_EXAMPLE_DRIFT` / `X_MANIFEST_STALE` from the `manifest` step | 11, 35 |
| 11 | `x verify --only boundaries,unit,contract,e2e` and fix what it fails: move an app wildcard off a `bearerMount` prefix, list every projected tool under a scope, give each offline scrape a `robots: { ignore }` reason, move a page's repo read into a query, pass an e2e's external `DATABASE_URL` / `S3_ENDPOINT` in `startE2eApp({ env })` | `X_ROUTE_CONFLICT`; `X_MCP_SCOPE_UNCOVERED` at boot; `X_SCRAPE_ROBOTS_DISALLOWED`; `X_BOUNDARY_ROUTE_TO_DB`; an e2e that read the external database's rows fails against the embedded one | 9, 19, 27, 30, 36 |
| 12 | read each script or CI job that runs `x shot --cdp-url` against a hosted browser, passes `--to` or `--dry-run` to `x jobs drain`, or reads `data.next` from `x jobs list --json`; **before the deploy,** read each worker Deployment that sets `WORKER_QUEUES` and list every queue it must serve; pass `concurrency: 5` to a hand-built `jobWorker` that must keep 5 slots | `X_CDP_CALL_FAILED` where a capture used to proceed; `X_CLI_BAD_FLAG`; `undefined` where a cursor was; `jobs.worker.queue-unserved` at a worker's boot and jobs left `ready` on the queues it no longer claims; 8 jobs at once where 5 ran | 7, 15, 29, 38, 39 |
| 13 | `x verify` | green, or a finding whose `fix:` is the edit | — |

### Entry by entry

Every package (1). Tier 0 — `@ultimat3/core` (2–5). Tier 1 and up — the factory spellings of
`db`, `cache`, `storage`, `auth`, `action`, `jobs`, `realtime`, `mail`, `notify`, `ai` and
`testing` (6). Tier 2 — `@ultimat3/entity` (7),
`@ultimat3/http` (8–9). Tier 3 — `@ultimat3/action` and `@ultimat3/query` (10–14),
`@ultimat3/jobs` (15–16), `@ultimat3/realtime` (17). Tier 4 — `@ultimat3/mcp` (18–19),
`@ultimat3/manifest` (20), `@ultimat3/notify` (21), `@ultimat3/ai` (22–24), `@ultimat3/ui` (25).
Tier 5 — `@ultimat3/testing` (26), `@ultimat3/scraping` (27), `@ultimat3/cli` (28–30). After
the cut — `@ultimat3/ai` (31), the value names of every package (32), `@ultimat3/jobs` (33).
Sweep 12c — `@ultimat3/core` (34), `@ultimat3/cli` (35), `@ultimat3/testing` (36); its review round —
`@ultimat3/auth` (37), `@ultimat3/cli` (38), `@ultimat3/jobs` (39), `@ultimat3/realtime` (40).

| # | Surface | Costs you an edit if |
|---|---|---|
| 1 | `engines.bun`, `x`, `x doctor` | you run Bun 1.4.0 or 1.4.1. `X_BUN_VERSION`; `bun upgrade`, and a 1.4.2 base image in the app's own Dockerfile |
| 2 | `app.config.ts`: any key it does not declare, at any depth; keys removed: `locales`, `defaultLocale`, `defaultTimeZone`, `defaultCurrency`, `theme.tokens`, `jobs.driver`, `ai.mcp.path`, and from earlier majors `realtime.heartbeatMs`, `database.urlEnv`, `database.poolSize`, `database.schema`, `pwa.installPrompt`, `auth.afterSignInPath`, `ai.modelEnv`, `cache.driver`, `cache.urlEnv`, `realtime.tier` | a layer writes one. `X_CONFIG_INVALID` naming the path and either its replacement or, for a key that never existed, the nearest real one (`did you mean drain?`); TS2353 in a typed config; TS2339 where code reads `config.locales`. The locales and their default are `defineCatalogs({ default, locales })` in `packages/i18n/src/index.ts`; the zone is passed per call, the currency per `Money`; the theme is `defineTheme` in `apps/web/shared/theme.ts`; the driver is `setJobDriver(postgresJobDriver({ executor }))`. Delete `ai.mcp.path`: every MCP endpoint, #0 included, mounts at its own `defineAppMcp({ path })` (default `/mcp`) — if yours was not `/mcp`, pass it as `path` to the first `defineAppMcp` in `apps/<app>/mcp.ts`. `McpConfig` is `{ expose }`. Of the ten older keys only `cache.driver` was read (→ `cache: { tiers: [...] }`); the rest were read by nothing — delete the line, and the `fix:` names what does the job (`REMOVED_CONFIG_KEYS`, `packages/core/src/config-removed.ts`) |
| 3 | `assertEnvExample`, `EnvExampleDriftError`, `resolveSpeculation` | you import one. `checkEnvExample(…)` returns findings instead of throwing — or let `x verify --only manifest` run it; speculation is `defineConfig({ navigation: { speculation } })` |
| 4 | `AuditRecord`, `normalizeAuditRecord`, `NormalizedAuditRecord` | a sink or test reads `record.action` or builds a record without `name` and `primitive`. TS2339 / TS2741. Read and write `name` and `primitive`; no DDL changed |
| 5 | `Deprecation`, `DeprecationField`, `DeprecationRender`, `recordDeprecatedCall`, `renderDeprecation`; the values `createClientFlight` (now `clientFlight`), `DEFAULT_CLIENT_RETRY`, `isSuperseded`, `isTransientFailure`, `getAuditSink`, `setAuditSink`, `resetAuditSink`; every other value a second package re-exported — the Entry 5 table below, including `formatIssues` (`mcp`), `MAX_MONEY_SCALE` (`money`), `ULTIMATE_ERROR_BRAND` (`core`), `JOB_STATES` and `TEST_TYPES` (`cli`) | you import one from a package that is not its home. TS2305; change the module specifier per the table — `@ultimat3/core` unless the row names another, and under core's own name where the row gives one. `action` and `query` re-export types only; scraping's `throwIfAborted(signal)` is `signal.throwIfAborted()` |
| 6 | every `createMemory*`, `createPg*`, `pgSchedulerState`, `createPostgresClient`; a `Memory*` / `InMemory*` / `Pg*` / `BuiltinAdapter` class `new`-ed as a value; every `create<Vendor>Driver`; storage's `memoryDriver`; every other `create<Thing>` factory; ai's `new AnthropicProvider` / `EchoProvider` / `HashEmbedder` / `RemoteEmbedder` / `BudgetLedger`, mcp's `new McpServer`, query's `new Builder` | you call one. TS2305 / TS2724; TS1485 / TS1362 where the class is now a type only. Rename per the table below. Arguments are unchanged; a type a factory takes or builds is renamed to the factory's spelling (TS2724 at the old name — the type-renames table below), so `import type { BuiltinAdapter }` is `PostgresAuthAdapter` and `MemoryAdapter` is `MemoryAuthAdapter`. A budget ledger is the gateway's (`gateway.callLedger(keys)`, `gateway.scope(…)`); a query builder is `from(…)`'s. `createBranch`, `createSession` and `createApp` keep their names: they are verbs |
| 7 | `Page` from `@ultimat3/entity` | you annotate a `findMany` or `.page()` result with it. Import `Page` from `@ultimat3/core` (or `@ultimat3/query`): `findMany` answers it, so the page now carries `hasMore` too. A hand-written `Repo` builds its page with core's `pageOf(rows, nextCursor)` (TS2741 / TS2322 at a `{ rows, nextCursor }` literal). **And** `ListPage` from `@ultimat3/storage`: `list()` answers `Page<StorageListEntry>` — read `rows` / `hasMore` / `nextCursor` for `objects` / `truncated` / `cursor` (`null` on the last page), pass `cursor: page.nextCursor`; a custom driver returns `pageOf(entries, nextCursor)`. **And** a script reading `x jobs list --json`: `data.next` is `data.nextCursor`, beside `data.hasMore` |
| 8 | `configureHttp({ drainTimeoutMs })`, `defineHttpConfig({ drainTimeoutMs })`; the same two with `buildIdHeader` | you pass one. `X_CONFIG_INVALID`, refused by key so a spread is caught too. `drainTimeoutMs`: declare `drain: { deadlineMs }` in `app.config.ts` — every role, web included, drains on it. `buildIdHeader`: delete it; the header is core's `BUILD_ID_HEADER` (`x-ultimate-build`), the only one any client sends |
| 9 | `bearerMount` | the app declares a wildcard at the mount's own `<prefix>/*rest`. `X_ROUTE_CONFLICT` at registration; the mount answers every method under its prefix |
| 10 | `.tool()`, `toMcpTool(s)`, `toQueryTool(s)`, `isExposed`, `toolFromAction`, `toolFromQuery`, `McpToolDescriptor`, `McpInvokeOptions`, `QueryTool*` | you project or call a tool by hand. `toolFrom(x)` for an action or a query (`toolFromAction` renamed, no alias), `toolListEntry(tool)` for the `tools/list` entry, `invoke(x, input, { ctx, surface: 'mcp' })` to call, `(await sourceFor(q, input, { surface: 'mcp' })).execute()` to read, `isMcpExposed(x.mcp)` from `@ultimat3/core` |
| 11 | query `Page`, the `?_first=` envelope, `openapi.json` | a client reads `endCursor` or `hasNextPage`. Read `nextCursor` and `hasMore`; pass `nextCursor` as `after` (`_after=` on the wire). Run `x manifest`. **And** a client keeps the LAST page's cursor — to poll for rows appended after it, or because it reads `nextCursor` without `hasMore`: the last page's `nextCursor` is now `null` (it is `null` exactly when `hasMore` is false, on every surface), so a `while (page.nextCursor)` loop no longer fetches an empty page past the end. To tail a listing, keep the cursor of the last page that had one, or re-read the first page |
| 12 | `http.rateLimit.buckets.<actionName>` | you limited an action through a named bucket. It is refused at boot with `X_CONFIG_INVALID` (a bucket keyed by a mounted action's or query's name): delete it from `configureHttp` and declare `rateLimit: { limit, windowMs }` on the action |
| 13 | `IdempotencyStore` | you wrote your own. Declare `keepsRedaction: true`, keep `settle`'s 4th argument `redacted` and return it as `IdempotencyRecord.redacted` |
| 14 | `SQL_*` exports of `action`, `jobs`, `notify`, `admin` | you import a statement other than a table DDL (`SQL_AUDIT_TABLE`, `SQL_IDEMPOTENCY_TABLE`, `SQL_JOBS_TABLE`, `SQL_NOTIFY_*_TABLE`, `SQL_ADMIN_AUDIT_TABLE`) — `SQL_CLAIM`, `SQL_OUTBOX_RELEASE` and `SQL_NOTIFY_INBOX_MARK_READ` included. Call the store that runs it, or copy the text into the app |
| 15 | `createNatsDriver`, `NatsDriverOptions`, `createRedisDriver`, `RedisDriverOptions`; the `--to` and `--dry-run` flags of `x jobs drain` | you import one — every method threw `X_NOT_IMPLEMENTED`. Postgres is the durable driver: `postgresJobDriver({ executor })`. Or a script passes either flag: `X_CLI_BAD_FLAG`; `x jobs drain` is still planned (`X_NOT_IMPLEMENTED`), so delete the call |
| 16 | `WebhookLedger` | you wrote your own. Add `isDisabled(endpointId): Promise<boolean>` |
| 17 | `ChangeEvent` | a custom change feed builds one. Pass `write: null` where it has no write name |
| 18 | `McpExposure` and its `name`; `QueryMcp`, `QueryMcpAnnotations`, `QueryListParams`, `QueryListFilterOp` (`query`); `McpAnnotationHints` (`action`) | a hand-built primitive handed to `toolFrom` sets `mcp: { name }` — rename the primitive instead. Or you name one of the types: import `McpExposureDeclaration`, `McpAnnotationHints`, `McpListParams`, `McpListFilterOp` from `@ultimat3/core`. An action's block is `ActionMcp`, `Omit<McpExposureDeclaration, 'listParams'>` |
| 19 | `defineAppMcp({ scopes })` | the map leaves a projected tool out — `include: 'exposed'` and hand-written tools such as `whoami` included. `X_MCP_SCOPE_UNCOVERED` at boot; list each tool under a scope |
| 20 | `contentHash` from `@ultimat3/manifest` | you import it. `fingerprint(body)` from `@ultimat3/core`, the same 16 hex characters |
| 21 | `DigestAppend`, `DigestStore` | you wrote a store. Key its replay on the required `appender` |
| 22 | `DEFAULT_MODEL`, a call with no model | a declaration, its prompt and the gateway all leave the model out. `X_AI_MODEL_UNRESOLVED` naming the three places; `echoProvider()` needs `model` too, and so does a request to a provider called directly (`anthropicProvider`, `openAiProvider`), which runs `request.model` and nothing else. `describeAgents()` answers `model: null` there |
| 23 | `anthropicProvider()` (24.x: `new AnthropicProvider()`), `ANTHROPIC_MODEL_IDS`, `OPENAI_MODEL_IDS` | you construct a provider without `models` or with the built-in list. TS2554 / TS2305; an empty list is `X_AI_REQUEST_INVALID`. Pass the ids the app registered |
| 24 | the built-in catalogue, `registerOpenAiModels()` | the app names a model it never `registerModel`-ed. `X_AI_MODEL_UNKNOWN` before any provider call. Register each model at boot, with its source and date beside each number |
| 25 | `Menu` trigger props | you type `aria-controls` as `string`. It is `string \| undefined`; a closed menu carries none |
| 26 | `FakeElement.listeners` | a test reads it. `element.listenerFor(name)`, or `island.fire(selector, type)` |
| 27 | an offline `scrape()` test with `fakeBrowser` / `fixtureBrowser` | it declares no `robots: { ignore }`. `X_SCRAPE_ROBOTS_DISALLOWED` under `bun test`; write the reason the run is permitted |
| 28 | `nearest` from `@ultimat3/cli` | you import it. `nearestName` from `@ultimat3/core` |
| 29 | `x shot --cdp-url` | the provider refuses the browser-target attach. `X_CDP_CALL_FAILED`; point at a browser that allows it, or drop the flag |
| 30 | the `boundaries` step | a `page`, `layout` or `route` imports a slice's `repo`. `X_BOUNDARY_ROUTE_TO_DB`; read through a query |
| 31 | `contentHash` from `@ultimat3/ai` | you import it. `promptHash(input)`, the same argument and the same hash; `contentHash` is `@ultimat3/render/server`'s byte hash only |
| 32 | a value name two packages declared: `forbidden`, `unauthenticated`, `json`, `text`, `validate`, `money`, `isEnabled`, `describeRoutes`, `channel`, `guard`, `guardBeforeInput`, `resetRegistry`, `explain`, `actorLabel`, `allowed`, `denied`, `relationsFor`, `invariant`, `uuid`, `cacheKeyFor`, `DEFAULT_RETRY`, `NO_TENANT`, `chunk`, `normalize`, `tokenize`, `introspect`, `isDestructive`, `assertNoDrift`, `safeUrl`, `formatBytes` (ui), `resetIdCounter`, `srcsetFor`, `frozenClock`, `testClock` (scraping), `sessionExpired` | you import one. TS2305 / TS2724; rename per the Entry 32 table below — same arguments, same answer, no alias. Scraping's `testClock` is `testScrapeClock`: `@ultimat3/testing`'s `testClock` is the former `createTestClock`, a different clock |
| 33 | `AckOptions.counted` | a custom `JobDriver.ack` reads it, or a caller passes it. TS2353 at a literal; delete it — every ack counts |
| 34 | `JobsConfig.concurrency` (`JobsConcurrency`) | code reads `config.jobs.concurrency` as a `number`. It is `number \| Readonly<Record<string, number>>`, slots per queue; narrow with `typeof concurrency === 'number'`, and read a queue's row as `concurrency[queue] ?? JOBS_CONCURRENCY_DEFAULT` (both exported by `@ultimat3/core`). An empty table, a blank queue name or a count below 1 is `X_CONFIG_INVALID` |
| 35 | `.env.example`, `x env example` | the app exports `envSchema`. The projection now ends in a `Framework` section — `ULTIMATE_CURSOR_SECRET`, `STORAGE_SIGNING_SECRET` — so the committed file is `X_ENV_EXAMPLE_DRIFT` in the `manifest` step; run `x env example` and commit it. A key the schema already declares is not repeated |
| 36 | `startE2eApp` | an e2e relied on an exported `DATABASE_URL` or `S3_ENDPOINT` reaching the app. Neither is inherited any more; pass it: `startE2eApp({ root, env: { DATABASE_URL: '<the external url>' } })` |
| 37 | `ResolveOAuthGrants` | code calls a `ResolveOAuthGrants`-typed function with one argument — typically an app's unit test of its grants function. TS2554; pass an `OAuthGrantContext` (`provider`, `tokens`, `idTokenClaims`, `userinfo()`) as the second argument. A one-argument implementation still assigns to the type |
| 38 | `WORKER_QUEUES` | a worker Deployment sets it and relied on also serving `jobs.queues` and every registered job's queue (24.x read nothing by that name). It is now exactly the listed queues: jobs on any other queue stay unclaimed by that worker, which warns `jobs.worker.queue-unserved` at boot naming them. List every queue the Deployment must serve, or unset the variable for the union. An empty entry (`banks,,mail`) is `X_CONFIG_INVALID` |
| 39 | `jobWorker({ concurrency })` | you build a `jobWorker` by hand and pass no `concurrency`, or a table that leaves a queue out. That queue runs `JOBS_CONCURRENCY_DEFAULT` (8, `@ultimat3/core`) slots, was 5; pass `concurrency: 5` to keep it. A worker `x` boots is unchanged |
| 40 | `ChangeEvent.entity` (`@ultimat3/realtime`) | a custom `ChangeFeed`, test or consumer builds or reads a change event. Rename `entity:` to `table:` — it holds the relation (table) name on every producer. TS2353 / TS2339. A sync node still reads a 24.x replicator's `entity` off the bus, so the rolling deploy needs no ordering |

Entry 6, the renames:

| Package | Was | Is |
|---|---|---|
| `db` | `createPostgresClient` | `postgresClient` |
| `cache` | `createMemorySemanticCache` | `memorySemanticCache` |
| `jobs` | `createMemoryDriver`, `createPgDriver` | `memoryJobDriver`, `postgresJobDriver` |
| `jobs` | `createPgLeader`, `createPgLeaseLeader`, `pgSchedulerState` | `postgresLeader`, `postgresLeaseLeader`, `postgresSchedulerState` |
| `jobs` | `createPgEventBus`, `createPgOutboxStore` | `postgresEventBus`, `postgresOutboxStore` |
| `jobs` | `createMemoryEventBus`, `createMemoryOutboxStore`, `createMemoryStepStore` | `memoryEventBus`, `memoryOutboxStore`, `memoryStepStore` |
| `jobs` | `createMemoryLeaseStore`, `createMemorySchedulerState`, `createMemoryBackfillLedger` | `memoryLeaseStore`, `memorySchedulerState`, `memoryBackfillLedger` |
| `mail` | `createMemoryDriver` | `memoryMailDriver` |
| `notify` | `createMemoryDeliveryLedger`, `createMemoryDigestStore`, `createMemoryInboxStore`, `createMemoryPreferenceStore` | `memoryDeliveryLedger`, `memoryDigestStore`, `memoryInboxStore`, `memoryPreferenceStore` |
| `notify` | `createPgDeliveryLedger`, `createPgDigestStore`, `createPgInboxStore` | `postgresDeliveryLedger`, `postgresDigestStore`, `postgresInboxStore` |
| `storage` | `memoryDriver`, `MemoryDriverOptions` | `memoryStorageDriver`, `MemoryStorageDriverOptions` |
| `auth` | `new BuiltinAdapter(…)`, `new MemoryAdapter(…)` | `postgresAuthAdapter(…)`, `memoryAuthAdapter(…)` |
| `action` | `new MemoryIdempotencyStore(…)` | `memoryIdempotencyStore(…)` |
| `realtime` | `new MemoryLocalStore()`, `new MemoryQueueStore()` | `memoryLocalStore()`, `memoryQueueStore()` |
| `realtime/server` | `new InMemoryAdvisoryLock(…)`, `new PgAdvisoryLock(…)` | `memoryAdvisoryLock(…)`, `postgresAdvisoryLock(…)` |
| `realtime/server` | `new InMemoryChangeFeed(…)`, `new PgLogicalReplicationFeed(…)`; `PgOutputDecoder` | `memoryChangeFeed(…)`, `postgresChangeFeed(…)`; not exported — the feed decodes |
| `mail` | `createSmtpDriver`, `createResendDriver`, `createLogDriver`, `createUnconfiguredDriver` | `smtpMailDriver`, `resendMailDriver`, `logMailDriver`, `unconfiguredMailDriver` |
| `ai` | `new PgVectorStore(…)`, `new MemoryVectorStore(…)`, `new MemoryBudgetStore()` | `postgresVectorStore(…)`, `memoryVectorStore(…)`, `memoryBudgetStore()` |
| `testing` | `createSubscribeDriver()` | `subscribeDriver()` |
| `auth` | `createAuthLimiter`, `createJwksClient`, `createKdfGate`, `createPkce`, `createTotpReplayGuard` | `memoryAuthLimiter`, `jwksClient`, `boundedKdfGate`, `pkcePair`, `memoryTotpReplayGuard` |
| `cache` | `createCacheStack`, `createCdnTier`, `createLruTier`, `createMemoTier`, `createRedisTier` | `cacheStack`, `cdnTier`, `lruTier`, `memoTier`, `redisTier` |
| `core` | `createClientFlight`, `createContext`, `createFence`, `createFlightGate` | `clientFlight`, `ctxOf`, `generationFence`, `flightGate` |
| `core` | `createLogger`, `createRaster`, `createSingleFlight` | `structuredLogger`, `blankRaster`, `singleFlight` |
| `db` | `createPgliteClient`, `createRecordingClient` | `pgliteClient`, `recordingClient` |
| `http` | `createPipeline`, `createRateLimiter`, `createRequestContext`, `createRouter`, `createServer` | `httpPipeline`, `rateLimiter`, `requestContext`, `httpRouter`, `httpServer` |
| `i18n` | `createTranslator` | `catalogTranslator` |
| `jobs` | `createJobsFacade`, `createLimiter`, `createOutboxRelay`, `createPacer` | `outboxJobsFacade`, `concurrencyLimiter`, `outboxRelay`, `backfillPacer` |
| `jobs` | `createScheduler`, `createStepRunner`, `createWorker` | `jobScheduler`, `stepRunner`, `jobWorker` |
| `realtime` | `createOutbox`, `createSyncNode` | `localOutbox`, `syncNode` |
| `realtime/server` | `createEntry`, `createFrameRouter`, `createReplicator` | `queryEntry`, `frameRouter`, `changeFeedReplicator` |
| `mcp` | `createDevServer`, `createMcpServer`, `new McpServer(…)` | `devMcpServer`, `mcpServer`, `mcpServer(…)` |
| `pwa` | `createInstallController` | `installController` |
| `render` | `createIslandCollector`, `createIsrController` | `islandCollector`, `isrController` |
| `ai` | `new AnthropicProvider(…)`, `new EchoProvider(…)`, `new HashEmbedder(…)`, `new RemoteEmbedder(…)` | `anthropicProvider(…)`, `echoProvider(…)`, `hashEmbedder(…)`, `remoteEmbedder(…)` |
| `ai` | `createGateway`, `CreateGatewayInput`; `new BudgetLedger(…)` | `providerGateway`, `ProviderGatewayInput`; `gateway.callLedger(keys)` or `gateway.scope(…)` |
| `ui` | `createFocusTrap`, `createFormBinding`, `createRovingTabindex`, `createToastStore` | `focusTrap`, `formBinding`, `rovingTabindex`, `toastStore` |
| `testing` | `createTestClock`, `createTestMail`, `createTestNetwork`, `createTestStatements` | `testClock`, `testMail`, `testNetwork`, `testStatements` |
| `testing` | `createRunJobs`, `createLiveNode`, `createTemplateSql` | `testJobs`, `liveNode`, `templateSql` |
| `scraping` | `createArtifactWriter`, `createPacer`, `createPrompt`, `createRing` | `artifactWriter`, `scrapePacer`, `scrapePrompt`, `boundedRing` |
| `scraping` | `createRobotsGate`, `createSecretBag`, `createUsageMeter` | `robotsGate`, `secretBag`, `usageMeter` |
| `query` | `new Builder(…)` | `from(…)` |

Each class above stays nameable as a type (`import type { MemoryVectorStore }`) — under the
factory's spelling where the two differed. Entry 6, the type renames (TS2724 at the old name):

| Package | Was | Is |
|---|---|---|
| `jobs` | `PgDriverOptions`, `MemoryDriverOptions`, `PgEventBusOptions` | `PostgresJobDriverOptions`, `MemoryJobDriverOptions`, `PostgresEventBusOptions` |
| `jobs` | `PgOutboxOptions`, `MemoryOutboxOptions`, `PgLeaseLeaderOptions` | `PostgresOutboxStoreOptions`, `MemoryOutboxStoreOptions`, `PostgresLeaseLeaderOptions` |
| `mcp` | `CreateMcpServerInput`, `CreateDevServerInput` | `McpServerInput`, `DevMcpServerInput` |
| `ai` | `PgVectorStore`, `PgVectorStoreInput`, `CreateGatewayInput` | `PostgresVectorStore`, `PostgresVectorStoreInput`, `ProviderGatewayInput` |
| `auth` | `BuiltinAdapter`, `MemoryAdapter` | `PostgresAuthAdapter`, `MemoryAuthAdapter` |
| `notify` | `PgDigestStore`, `PgDigestStoreOptions`, `PgInboxStore`, `PgInboxStoreOptions` | `PostgresDigestStore`, `PostgresDigestStoreOptions`, `PostgresInboxStore`, `PostgresInboxStoreOptions` |
| `notify` | `PgDeliveryLedger`, `PgDeliveryLedgerOptions`, `MemoryLedgerOptions` | `PostgresDeliveryLedger`, `PostgresDeliveryLedgerOptions`, `MemoryDeliveryLedgerOptions` |
| `realtime/server` | `InMemoryAdvisoryLock`, `PgAdvisoryLock`, `PgAdvisoryLockOptions` | `MemoryAdvisoryLock`, `PostgresAdvisoryLock`, `PostgresAdvisoryLockOptions` |
| `realtime/server` | `InMemoryChangeFeed`, `InMemoryChangeFeedOptions`, `PgLogicalReplicationFeed`, `PgLogicalReplicationOptions` | `MemoryChangeFeed`, `MemoryChangeFeedOptions`, `PostgresChangeFeed`, `PostgresChangeFeedOptions` |
| `storage` | `MemoryDriverOptions` | `MemoryStorageDriverOptions` |

Entry 5, each value at its home — `@ultimat3/core` unless the right-hand column names another:

| Was imported from | Values | Import from |
|---|---|---|
| `action` | `BUILD_ID_HEADER`, `IDEMPOTENCY_HEADER`, `pluralize`, `actorOf` | `@ultimat3/core` |
| `action` | `toBucket` | `@ultimat3/http` |
| `action`, `query` | `admitsAnonymous`, `policyPermissions`, `policyCapability` | `@ultimat3/policy` |
| `query` | `CursorInvalidError`, `actorOf`; `derivePath` | `@ultimat3/core`; as `queryPath` |
| `query` | `MAX_PAGE_SIZE` | `@ultimat3/entity` |
| `http` | `escapeHtml`, `readCookie`, `REQUEST_TIMEOUT_HEADER`, `isCanonicalWebhookField`, `WEBHOOK_ID_HEADER`, `WEBHOOK_SIGNATURE_HEADER`, `WEBHOOK_SIGNATURE_VERSION`, `WEBHOOK_TOPIC_HEADER` | `@ultimat3/core` |
| `jobs` | `WEBHOOK_FIELD_MAX`, `webhookHeaders`, `webhookMac`, `webhookSignature`, `webhookSigningString`, `classifyThrown`; types `WebhookMacInput`, `WebhookSigningInput` | `@ultimat3/core` |
| `flags` · `time` · `auth`, `storage` | `fnv1a` · `localeInvalid` · `timingSafeEqual` | `@ultimat3/core` |
| `i18n` | `DEFAULT_LOCALE`, `directionOf`, `isRtl`, `localeSegment`; `FRAMEWORK_CATALOG_LOCALE` | `@ultimat3/core`; as `DEFAULT_LOCALE` |
| `mail` | `escapeHtml`; `MAIL_CATALOG_LOCALE` | `@ultimat3/core`; as `DEFAULT_LOCALE` |
| `ai` | `MAX_SEMANTIC_CACHE_SCOPES` | `@ultimat3/core`, as `MAX_CACHED_FORMATTERS` |
| `cache` | `TIER_ORDER`; `createSingleFlight`, types `FlightJoin`, `SingleFlight` | `@ultimat3/core`, as `CACHE_TIERS`; as `singleFlight` |
| `render` | `formatBytes`, `RENDER_MODES`, `HYDRATE_STRATEGIES`, `OFFLINE_STRATEGIES`, `THEME_STORAGE_KEY`, `CLIENT_PATH_STYLE_META`, `CLIENT_PERSIST_META`, `CLIENT_SCOPE_META`, `CLIENT_BUILD_META`, `CLIENT_SYNC_META`, `CLIENT_SYNC_WORKER_META` | `@ultimat3/core` |
| `render` | `NAVIGATION_HEADER`, `NAVIGATION_SURFACE_HEADER`, `NAVIGATION_SCOPE_HEADER`, `NAVIGATION_LOCATION_HEADER` | `@ultimat3/core`, as `CLIENT_NAVIGATION_HEADER`, `CLIENT_NAVIGATION_SURFACE_HEADER`, `CLIENT_NAVIGATION_SCOPE_HEADER`, `CLIENT_NAVIGATION_LOCATION_HEADER` |
| `render` | `tagKeys` | `@ultimat3/cache` |
| `pwa` | `formatBytes`, `BUILD_ID_HEADER`; `BUILD_ID_META`; `APP_UPDATE_AVAILABLE` | `@ultimat3/core`; as `CLIENT_BUILD_META`; as `APP_UPDATE_MESSAGE` |
| `ui` | `THEME_STORAGE_KEY` | `@ultimat3/core` |
| `admin` | `REDACTED`; `ADMIN_AUDIT_TABLE`, `SQL_ADMIN_AUDIT_TABLE` | `@ultimat3/core`; `@ultimat3/admin/schema` |
| `realtime` | `isJsonObject` | `@ultimat3/core` |
| `scraping` | `ANY_HOST`, `hostDecision`, `hostMatches`; types `HostRule`, `HostDecision` | `@ultimat3/core` |
| `scraping` | `DEFAULT_CONTENT_TYPE` | `@ultimat3/storage` |
| `scraping` | `throwIfAborted(signal)` | nowhere: `signal.throwIfAborted()` |
| `mcp` · `money` · `core` | `formatIssues` · `MAX_MONEY_SCALE` · `ULTIMATE_ERROR_BRAND` | `@ultimat3/schema` |
| `cli` | `JOB_STATES` | `@ultimat3/jobs` |
| `cli` | `TEST_TYPES` | `@ultimat3/testing` |

Two re-exports stay, pinned in `scripts/lib/package-reexports.ts`: schema's `t` through the eight
primitive barrels (`action`, `ai`, `entity`, `jobs`, `mail`, `mcp`, `notify`, `query`), and core's
`describeValue` and `isIsoDateTime` over the declared `core → schema` edge. `bun run flight-copies`
refuses any other (`X_HELPER_COPY`), and a blind `export *` in shipped package source.

Entry 32, one name per meaning:

| Package | Was | Is |
|---|---|---|
| `auth` | `forbidden`, `unauthenticated` | `authForbidden`, `authUnauthenticated` |
| `policy` | `forbidden` | `policyForbidden` |
| `http` | `json`, `text`, `validate` | `jsonResponse`, `textResponse`, `validateBody` |
| `money` · `pwa` · `render` · `notify` | `money()` · `isEnabled` · `describeRoutes` · `channel` | `fromMinor()` · `hasCapability` · `describePages` · `deliveryChannel` |
| `action` | `guard`, `guardBeforeInput`, `resetRegistry` | `guardAction`, `guardActionBeforeInput`, `resetActions` |
| `query` | `guard`, `guardBeforeInput`, `resetRegistry`, `explain` | `guardQuery`, `guardQueryBeforeInput`, `resetQueries`, `explainQuery` |
| `admin` | `actorLabel`, `allowed`, `denied`, `relationsFor` | `adminActorLabel`, `adminAllowed`, `adminDenied`, `relationDataFor` |
| `core` | `invariant`, `InvariantOptions`, `uuid` | `assertCoded`, `AssertCodedOptions`, `uuidV7` |
| `ai` | `cacheKeyFor`, `DEFAULT_RETRY`, `NO_TENANT` | `promptCacheKey`, `DEFAULT_GATEWAY_RETRY`, `NO_VECTOR_TENANT` |
| `ai` | `chunk`, `normalize`, `tokenize` | `chunkDocument`, `normalizeVector`, `wordTokens` |
| `db` | `introspect`, `isDestructive`, `assertNoDrift` | `introspectSchema`, `isDestructiveMigration`, `assertNoSchemaDrift` |
| `mail` | `safeUrl` | `safeMailHref` |
| `ui` | `formatBytes`, `resetIdCounter`, `srcsetFor` | `formatFileSize`, `resetUseIdCounter`, `variantSrcset` |
| `testing` | `frozenClock(now, body)` | `withFrozenClock(now, body)` |
| `scraping` | `testClock`, `sessionExpired` | `testScrapeClock`, `scrapeSessionExpired` |

`@ultimat3/i18n`'s `t('key')` and `@ultimat3/schema`'s `t` keep one name on purpose: no file imports
both (`scripts/factory-names-pins.ts` holds the measurement).

### Before → after

Entry 2 — `app.config.ts`, and where each key went:

```diff
 export const config = defineConfig({
   name: 'my-app',
-  locales: ['en', 'es'],
-  defaultLocale: 'en',
-  defaultTimeZone: 'UTC',
-  defaultCurrency: 'USD',
-  theme: { defaultMode: 'system', tokens: { brand: '#0a84ff' } },
-  jobs: { driver: 'postgres' },
-  ai: { mcp: { expose: true, path: '/agents' } },
+  theme: { defaultMode: 'system' },
+  ai: { mcp: { expose: true } },
+  drain: { deadlineMs: 25_000 }, // entry 8: was configureHttp({ drainTimeoutMs })
 });

 // apps/web/mcp.ts — an endpoint's path is its own; /mcp needs no line
+export const mcp = defineAppMcp({ path: '/agents', /* name, tools, resolveToken … */ });

 // packages/i18n/src/index.ts — the one declaration of the locales and their default
+export const catalogs = defineCatalogs({ default: 'en', locales: { en, es } });

 // at each format call — there is no ambient zone
-formatDate(at, { locale });
+formatDate(at, { locale, zone: 'Europe/Paris' });
```

Entry 10 — the one MCP projection:

```diff
-import { toMcpTool, isExposed } from '@ultimat3/action';
-import { toQueryTool } from '@ultimat3/query';
+import { invoke } from '@ultimat3/action';
+import { isMcpExposed } from '@ultimat3/core';
+import { toolFrom, toolListEntry } from '@ultimat3/mcp';
+import { sourceFor } from '@ultimat3/query';

-const entry = publishPost.tool();                       // or toMcpTool(publishPost)
+const entry = toolListEntry(toolFrom(publishPost)); // an action or a query
-await toMcpTool(publishPost).invoke(input, { ctx });
+await invoke(publishPost, input, { ctx, surface: 'mcp' });
-await toQueryTool(postList).read(input);
+await (await sourceFor(postList, input, { surface: 'mcp' })).execute();
-if (isExposed(publishPost)) …
+if (isMcpExposed(publishPost.mcp)) …
```

Entry 11 — a page:

```diff
 const page = await client.postList({ _first: 20 });
-if (page.hasNextPage) next = page.endCursor;
+next = page.nextCursor; // null exactly when !page.hasMore — the last page carries no cursor
```

Entry 12 — a per-action limit:

```diff
-configureHttp({ rateLimit: { buckets: { publishPost: { capacity: 10, refillPerSecond: 0.1 } } } });
 export const publishPost = action({
   input: t.object({ id: t.uuid }),
+  rateLimit: { limit: 10, windowMs: 60_000 },
   …
 });
```

Entries 4, 13, 16, 17 and 21 — the members a custom implementation owes:

```diff
-const record = { action: 'publishPost', /* … */ };
+const record = { name: 'publishPost', primitive: 'action', /* … */ };

 const store: IdempotencyStore = {
+  keepsRedaction: true,
-  settle: (key, value, reservationId) => …,
+  settle: (key, value, reservationId, redacted) => …, // keep it; return it as record.redacted
 };

 const ledger: WebhookLedger = {
+  isDisabled: (endpointId) => Promise.resolve(disabled.has(endpointId)),
 };

-await feed.emit({ entity, op, before, after, lsn, txid, orgId, at });
+await feed.emit({ entity, op, before, after, lsn, txid, orgId, at, write: null });
```

Entries 22–24 — the app brings its models. Every number below is a placeholder: copy the vendor's
published limits and prices into the app's own `models.ts`, with the source and the date beside
each, as `examples/dummy/apps/web/app/models.ts` does.

```ts
import { anthropicProvider, configureAi, providerGateway, registerModel } from '@ultimat3/ai';

const usd = (minor: number) => ({ minor, currency: 'USD' }) as const;

export const appModel = registerModel({
  id: 'your-model-id',
  family: 'your-vendor',
  contextWindow: 200_000,
  maxOutput: 32_000,
  inputPerMillion: usd(300),
  outputPerMillion: usd(1_500),
  cacheMinimumTokens: 1_024,
  reasoning: { effort: true, adaptive: true, disableThinkingUpTo: undefined },
});

configureAi({
  gateway: providerGateway({
    providers: [anthropicProvider({ models: [appModel.id] })],
    defaultModel: appModel.id,
  }),
});
```

```diff
-new AnthropicProvider()
+anthropicProvider({ models: [appModel.id] })                 // entry 6: a factory, not a class
-openAiProvider({ apiKey, models: [...OPENAI_MODEL_IDS] })
+openAiProvider({ apiKey, models: [appModel.id] })
-beforeEach(() => registerOpenAiModels());
+beforeEach(() => registerModel(testModelRow));
-new EchoProvider().generate({ prompt })
+echoProvider().generate({ prompt, model: appModel.id })
-createGateway({ providers, defaultModel })
+providerGateway({ providers, defaultModel })
```

Entries 6 and 32 — a factory name and three value names, after the edit:

```ts
import { structuredLogger, uuidV7 } from '@ultimat3/core';
import { jsonResponse } from '@ultimat3/http';
import { fromMinor } from '@ultimat3/money';

const log = structuredLogger(); // was createLogger()

export function quote(): Response {
  const id = uuidV7(); // was uuid()
  const price = fromMinor(1_999, 'USD'); // was money(1_999, 'USD')
  log.info('quoted', { id, minor: price.minor });
  return jsonResponse({ id, price }); // was json({ id, price })
}
```

Entry 7 — a storage page and the `x jobs list --json` page:

```diff
 let cursor: string | null = null;
 do {
   const page = await disk.list({ prefix: 'avatars/', cursor });
-  for (const entry of page.objects) seen.push(entry.key);
-  cursor = page.truncated ? page.cursor : null;
+  for (const entry of page.rows) seen.push(entry.key);
+  cursor = page.nextCursor; // null exactly when !page.hasMore
 } while (cursor !== null);

-x jobs list --json | jq -r '.data.next'
+x jobs list --json | jq -r 'select(.data.hasMore) | .data.nextCursor'
```

### Not breaking, but you will see it

| Surface | What changed |
|---|---|
| `x shot`, the PWA manifests, the service worker, the prerender, the speculation rules, the sitemap, `x g --locales`, `x i18n`, the e2e default locale | read the locales from the app's `defineCatalogs()` through one reader, `appLocaleSet(root)` (`@ultimat3/i18n/app-catalogs`), never `app.config.ts` — an app with no `packages/i18n/src/index.ts` is `en` alone on every one of them (`UNDECLARED_LOCALES`) |
| `x jobs list` | a full last page no longer prints `--after <cursor>` to an empty page |
| Helm grace periods | sized from `drain.deadlineMs` (the worker's from `drain.workerDeadlineSeconds` when > 0, since 26.0.0), which `x deploy --method helm` passes on every upgrade |
| `x jobs drain` | still planned: `X_NOT_IMPLEMENTED` before it boots the queue or leases a job; it takes no `--to` or `--dry-run` (entry 15), since no durable second driver ships |
| `describeAgents()` | `model` and `modelFrom` are `null` where nothing names a model; `'built-in-default'` is gone |
| fix lines | no `fix:` names a vendor's model as the default |
| `pwa.offline.fallback`, `.image`, `.font` | must be paths on this origin: `//host`, `/\host` and absolute URLs are `X_CONFIG_INVALID` at config load |
| a raw MCP tool with no `destructive` | billed to the write bucket and listed as `(action)`; set `destructive: false` on a hand-registered read |
| `BarChart` | renders a `<figure>` around `<svg role="img">`; `class` lands on the figure |
| `x shot --matrix` | directories are `<theme>-<w>x<h>`, were `<theme>-<w>` |
| `/admin` home | counts a resource only when it declares `count: true` |
| `x env check`, `x doctor` | report `ULTIMATE_CURSOR_SECRET` and `STORAGE_SIGNING_SECRET` off one list, the published development cursor key counted as unset; `x env check` judges a table as the boot does (a table naming no environment is deployed), `x doctor` only where this machine's environment is named `staging` or `production`; neither reports the storage key when `S3_ENDPOINT` selects object storage. `X_CURSOR_SECRET_DEV` at boot prints the same fix, `export ULTIMATE_CURSOR_SECRET="$(openssl rand -hex 32)"` |
| `ROLE=worker` on SIGUSR2 | retires: stops claiming, finishes every held job, aborts nothing, logs `jobs.worker.retiring` / `jobs.worker.retired`, drains, exits 0. Both Helm charts send it from a worker `preStop` when `roles.worker.retireSeconds` > 0 (default `0`, off) and add those seconds to the worker's grace period. A no-op on Windows |
| a table two producer kinds publish | a sync node keeps the kind it read first for that table and drops the other's changes, logged once as `X_REALTIME_PRODUCER_CONFLICT` |
| `x dev` restart | retires the worker first and waits, on stderr, for its running jobs to finish |

### Where the sites are

```sh
grep -rnwE "locales|defaultLocale|defaultTimeZone|defaultCurrency|tokens|driver|drainTimeoutMs|buildIdHeader|heartbeatMs|urlEnv|poolSize|schema|installPrompt|afterSignInPath|modelEnv|tier" app.config.ts apps packages --include=*.ts
grep -n "mcp" app.config.ts
grep -rnwE "createMemory[A-Z]\w*|createPg[A-Z]\w*|pgSchedulerState|createPostgresClient|create[A-Z]\w*Driver|(Nats|Redis|Memory)DriverOptions|memoryDriver|MemoryIdempotencyStore|(Pg|Memory)VectorStore|MemoryBudgetStore|(Builtin|Memory)Adapter|Memory(Local|Queue)Store|(InMemory|Pg)AdvisoryLock|InMemoryChangeFeed|PgLogicalReplicationFeed|PgOutputDecoder" apps packages --include=*.ts --include=*.tsx
grep -rnwE "assertEnvExample|EnvExampleDriftError|resolveSpeculation|normalizeAuditRecord|NormalizedAuditRecord|contentHash|nearest" apps packages scripts --include=*.ts --include=*.tsx
grep -rnwE "create(AuthLimiter|JwksClient|KdfGate|Pkce|TotpReplayGuard|CacheStack|CdnTier|LruTier|MemoTier|RedisTier|ClientFlight|Context|Fence|FlightGate|Logger|Raster|SingleFlight|Pipeline|RateLimiter|RequestContext|Router|Server|Translator|JobsFacade|Limiter|OutboxRelay|Pacer|Scheduler|StepRunner|Worker|Outbox|SyncNode|Entry|FrameRouter|Replicator|DevServer|McpServer|InstallController|IslandCollector|IsrController|Gateway|PgliteClient|RecordingClient|FocusTrap|FormBinding|RovingTabindex|ToastStore|TestClock|TestMail|TestNetwork|TestStatements|RunJobs|LiveNode|TemplateSql|ArtifactWriter|Prompt|Ring|RobotsGate|SecretBag|UsageMeter)|CreateGatewayInput|AnthropicProvider|EchoProvider|HashEmbedder|RemoteEmbedder|BudgetLedger|McpServer|Builder|authLimiter|totpReplayGuard" apps packages --include=*.ts --include=*.tsx
grep -rnwE "PgDriverOptions|MemoryDriverOptions|PgEventBusOptions|PgOutboxOptions|MemoryOutboxOptions|PgLeaseLeaderOptions|CreateMcpServerInput|CreateDevServerInput|PgVectorStore(Input)?|BuiltinAdapter|MemoryAdapter|Pg(DigestStore|InboxStore|DeliveryLedger)(Options)?|MemoryLedgerOptions|InMemoryAdvisoryLock|InMemoryChangeFeed(Options)?|PgAdvisoryLock(Options)?|PgLogicalReplication(Feed|Options)" apps packages --include=*.ts --include=*.tsx
grep -rnwE "BUILD_ID_HEADER|IDEMPOTENCY_HEADER|pluralize|toBucket|admitsAnonymous|policyPermissions|policyCapability|actorOf|CursorInvalidError|MAX_PAGE_SIZE|derivePath|escapeHtml|readCookie|REQUEST_TIMEOUT_HEADER|isCanonicalWebhookField|WEBHOOK_[A-Z_]+|webhook(Headers|Mac|Signature|SigningString)|Webhook(Mac|Signing)Input|classifyThrown|fnv1a|localeInvalid|timingSafeEqual|DEFAULT_LOCALE|directionOf|isRtl|localeSegment|FRAMEWORK_CATALOG_LOCALE|MAIL_CATALOG_LOCALE|MAX_SEMANTIC_CACHE_SCOPES|FlightJoin|SingleFlight|TIER_ORDER|formatBytes|RENDER_MODES|HYDRATE_STRATEGIES|OFFLINE_STRATEGIES|THEME_STORAGE_KEY|CLIENT_[A-Z_]+_META|NAVIGATION_(SURFACE_|SCOPE_|LOCATION_)?HEADER|tagKeys|BUILD_ID_META|APP_UPDATE_AVAILABLE|REDACTED|(SQL_)?ADMIN_AUDIT_TABLE|isJsonObject|ANY_HOST|host(Decision|Matches)|Host(Rule|Decision)|throwIfAborted|DEFAULT_CONTENT_TYPE|formatIssues|MAX_MONEY_SCALE|ULTIMATE_ERROR_BRAND|JOB_STATES|TEST_TYPES" apps packages --include=*.ts --include=*.tsx
grep -rnwE "forbidden|unauthenticated|json|text|validate|money|isEnabled|describeRoutes|channel|guard|guardBeforeInput|resetRegistry|explain|actorLabel|allowed|denied|relationsFor|invariant|InvariantOptions|uuid|cacheKeyFor|DEFAULT_RETRY|NO_TENANT|chunk|normalize|tokenize|introspect|isDestructive|assertNoDrift|safeUrl|resetIdCounter|srcsetFor|frozenClock|testClock|sessionExpired" apps packages --include=*.ts --include=*.tsx | grep "@ultimat3/"
grep -rnwE "McpExposure|QueryMcp|QueryMcpAnnotations|QueryListParams|QueryListFilterOp|McpAnnotationHints|ListPage|truncated|counted" apps packages --include=*.ts --include=*.tsx
grep -rnE "(Deprecation\w*|recordDeprecatedCall|renderDeprecation|createClientFlight|DEFAULT_CLIENT_RETRY|isSuperseded|isTransientFailure|(get|set|reset)AuditSink).*from '@ultimat3/(action|query)'" apps packages --include=*.ts --include=*.tsx
grep -rnE "\.tool\(\)|toMcpTools?\b|toQueryTools?\b|isExposed\b|toolFromAction|toolFromQuery|McpToolDescriptor|McpInvokeOptions|QueryTool(Descriptor|ReadOptions|Answer)" apps packages --include=*.ts --include=*.tsx
grep -rnwE "endCursor|hasNextPage|SQL_[A-Z_]+|AuditRecord|IdempotencyStore|WebhookLedger|ChangeEvent|DigestAppend|DigestStore|FakeElement" apps packages --include=*.ts --include=*.tsx
grep -rnE "Page\b.*from '@ultimat3/entity'|bearerMount\(|scopes:|buckets:|aria-controls|robots:|fakeBrowser\(|fixtureBrowser\(" apps packages --include=*.ts --include=*.tsx
grep -rnwE "DEFAULT_MODEL|ANTHROPIC_MODEL_IDS|OPENAI_MODEL_IDS|registerOpenAiModels|AnthropicProvider|EchoProvider|defaultModel|registerModel" apps packages --include=*.ts --include=*.tsx
grep -rnE "cdp-url|x shot|jobs drain|jobs (ls|list)|data\.next\b" .github scripts package.json bin
grep -rnE "jobs\.concurrency|startE2eApp\(|jobWorker\(|ResolveOAuthGrants|resolveGrants" apps packages --include=*.ts --include=*.tsx
grep -rn "WORKER_QUEUES" . --include=*.yaml --include=*.yml --include=*.env --include=Dockerfile* --exclude-dir=node_modules
```

The `typecheck` step finds 3–7, 10, 11, 13–18, 20, 21, 23, 25–28, 31–34, 37 and 40 at the import or
the member, and 2 in a typed `app.config.ts`. Entries 2 and 8 also refuse at the first import of the
config (`X_CONFIG_INVALID`), 19 at boot, 9 at route registration and 22 and 24 at the first model
call. The `manifest` step finds 11's stale `openapi.json` and 35's stale `.env.example`, `boundaries` finds 30, and the unit and
e2e suites find 27. Nothing finds 12 — a bucket keyed by an action's name is read by nobody — or 29
until a capture runs; read each `configureHttp` for 12 and each `x shot --cdp-url` caller for 29.
A shell script that parses `x jobs list --json` (7) or passes `--to` to `x jobs drain` (15) fails only
when it runs: the last grep above finds it. Nothing finds 36 but an e2e that reads the external
database's rows; the `startE2eApp` grep finds each caller. Nothing finds 38 or 39 either — a worker
leaves an unlisted queue's jobs `ready` with one boot warning, and a hand-built `jobWorker` simply
runs more at once — so the last two greps list every `WORKER_QUEUES` and every `jobWorker(` to read.
Entry 1 is `x doctor`'s first line.
