Skip to content

Commit 7c5a311

Browse files
fix(runtime): runtime refusals, warnings and route-ledger notes state each decision in words instead of a tracker number (stage 3) (#21219)
Part of #20752 Clause-②: no **Stage 3 of 5 of the `domain:cli` lane under the maintainer's A / A ruling (5902360492): the `packages/runtime` strings.** The card stays open for stages 4 and 5, so this PR carries no closing keyword. Text only: no status, error `code`, field, route, export or control flow moves, and the dispatcher error vocabulary keeps every key and code. ## What this does Refusals, boot errors, log lines, endpoint hints, the dispatcher error vocabulary's `why` text and the route ledger's notes in `packages/runtime` sent the reader to a tracker number for the reason behind them. In form D, as stages 1 and 2 (PR #21172, PR #21188) applied it, the number goes. Where the sentence already said what was decided, only the citation goes. Where it leaned on the number, it now says the decision in words. All 67 ledgered occurrences in `packages/runtime` (claim `5935998841`), in 55 string sites: `route-ledger.ts` 42, `dispatcher-error-vocabulary.ts` 10, `standalone-stack.ts` 3, `sandbox/body-runner.ts` 3, `endpoint-executor.ts` 2, `action-execution.ts` 2, and one each in `resolve-project-database.ts`, `endpoint-policy.ts`, `domains/auth.ts`, `domains/activation-gate.ts` (the fold site from the dead-citation sweep) and `app-plugin.ts`. Every cited card was read first. Three answer 404 and were read through the commit that decided them: 8885 (`30b1c636a2`, which registered the nine template-generated approvals codes), 10243 (`266436a7f`, which made flow enablement an authoring write) and 10179 (`53a48c93f4`, through stage 2's wording). Two are objectui cards (6593, 5933) that this session cannot open; they were read through the records in this repository that cite them (see Acceptance notes). ### Rewritten in words | Where (head line) | Cited | The text now says | Decision read from | |---|---|---|---| | `action-execution.ts:2476` doubled-navigation warning | 11519 | "...two post-success destinations for one success, a pair the contract refuses rather than ranks." | ruling 5393507405 (refuse the doubled channel, no precedence field) | | `action-execution.ts:2477` same warning | objectui 5933 | "...is ignored (the interim precedence the console renderer applies, which no contract promises)" | card 11519 body and ruling 5393507405; the spec refusal's own wording, "the interim precedence" | | `app-plugin.ts:1781` seed tenancy warning | 8686 | "seed tenancy handoff failed: the seed rows were not stamped with the new organization, so seed and API writes stay on separate autonumber counters until the next boot's migration repairs it" | ruling 5299880350 (seed writes carry the organization the way API writes do; one counter per object) | | `dispatcher-error-vocabulary.ts:349` `APPROVAL_*_FAILED` row | 8885 (404) | "All nine codes the family produces are registered in the ledger, and this row's pin is what keeps that true" | landing commit `30b1c636a2` | | `dispatcher-error-vocabulary.ts:356` same row | 9223 | "the scan reports a template-spelled code under its family identity rather than dropping it, so it SEES the template" | os-dev-report 5312437754, ACCEPT 5312453178 (`objlittemplate` shape) | | `dispatcher-error-vocabulary.ts:520` `YOU_CANNOT_IMPERSONATE_ADMINS` row | 9968 | "The in-repo re-implementation of the vendor's impersonation handler, which admits an ADR-0068 platform admin, makes it reachable for the first time" | rulings 5353915975, 5380748215; contract review 5363785802 | | `dispatcher-error-vocabulary.ts:597` `OS_METADATA_CONVERTED` row | 12772 | "the artifact-ingestion forward-conversion policy, which runs the ADR-0087 conversions over an artifact built by older tooling before its strict parse" | os-dev-report 5448148598, review 5447759290 | | `dispatcher-error-vocabulary.ts:680` `owd_widening_forbidden` row | 9232 | "it rides the wire in TWO fields because the flat REST door narrows like every other door" | ruling 5315734845 (the flat door demotes too) | | `dispatcher-error-vocabulary.ts:690` same row | 9460 | "Invisible to BOTH vocabulary gates until the scan learned the code-carrying helper shape" | ACCEPT 5327577494, landing note 5327948790 (`codehelper` shape) | | `dispatcher-error-vocabulary.ts:696` same row | 9106, 9460 half (2) | "the call between renaming it and keeping the demote (the closed member in `code`, this spelling in `declaredCode`) is the `packages/spec` lane's; until that lane registers a code the standing demote answers this spelling, and the call is NOT decided here" | ruling 5307569301 (demote to `declaredCode`); triage 5326829216 (half 2 settled by the standing rule, registration is the spec lane's) | | `domains/activation-gate.ts:279` enablement refusal | 10243 (404) | "...for as long as it stays off, and the switch is not scoped to the caller's organization." | commit `266436a7f` (no organization wall scopes the enabled bit; one activation row per deployment) | | `domains/auth.ts:143` sanitised-500 log line | 5085 | "...answered with a sanitised 500: the message is withheld unconditionally, and this line is where the original error is read" | ACCEPT 5200514639 | | `resolve-project-database.ts:314` legacy-file notice | 6469 | "Reading legacy database file ... — dev, start and migrate now share one default, ...; migrate with: ..." | ruling 5225112344 (one default, legacy files read with a loud notice) | | `sandbox/body-runner.ts:126` missing-logger warning | 7448 | "Pass `logger` to ...BodyRunnerFactory({ … }): the capability writes only to that logger, never to `console`, so the host's level and sinks apply." | report 5251672873 (serve the capability from the factory's logger) and the function's own docblock | | `standalone-stack.ts:675` no-dispatch-arm guard | 3276 | "falling through to SQLite would hand the caller a database engine they never selected." | card body; commit `cfb549db8` | | `route-ledger.ts:321` discovery note | 5682 (a PR) | "...holds to the double assertion: the value parses against `DiscoverySchema`, and it carries no key the protocol does not declare" | PR 5682 body, comment 5198848731; stage 2's REST wording | | `route-ledger.ts:330`, `:332` analytics notes | 3584 | "...so the client moved to this route rather than the dispatcher growing an alias" / "...so the client aligned to the dispatcher rather than the dispatcher growing an alias" | landing commit `0bab8bb454` (analytics: client aligns to the dispatcher) | | `route-ledger.ts:402` publish note | 12038 ("ruling 5A") | "...re-exported into `/api`, never declared there a second time" | ruling 5434804846 item 5A; stage 2's wording | | `route-ledger.ts:405` publish-drafts note | 9406 ruling | "`probes` is deliberately opaque in the declaration, upgraded to a modeled schema only when a consumer needs a field of it" | ruling 5322875103 | | `route-ledger.ts:427` duplicate note | objectui 6593 | "(a console that read the envelope `success` reported a partial or empty duplicate as done)" | `DuplicatePackageResponseSchema` docblock and the client's `duplicate` comment, which record that defect | | `route-ledger.ts:435`, `:441` automation notes | 10243 ruling | "since the ruling that enablement is an authoring write" / "since the 2026-08-23 ruling that enablement is an authoring write" | commit `266436a7f` | | `route-ledger.ts:441` toggle note | 10145 | "The definition-write gate deliberately left this one out as engine state and filed the question" | landing commit `128684d500` ("toggle ... filed separately rather than folded into a security fix") | | `route-ledger.ts:435`, `:441` | 5519 | "the domain-wide anonymous floor" | card 5519 (anonymous 401 gate before dispatch, ACCEPT 5195955348); the clone row's existing wording | | `route-ledger.ts:497` dispatcher PUT note | 12702 | stage 2's twin wording: "...own active organization, so a tenant org admin authors their own org's overlays without platform-wide `manage_metadata`" | ACCEPT 5439334711 | | `route-ledger.ts:507` migrate-stored note | 4327 | stage 2's twin wording: "ADR-0087 stored-row canonicalization, the route form of `os migrate meta --stored`: it rewrites stored `sys_metadata` rows in place to their canonical form" | card body; commits `83cf2d3082`, `8aacf9456c` | | `route-ledger.ts:509` state/:field note | 9180 (x2), 10179 (404) | stage 2's twin wording: "Step 2 of the singular-segment ruling ...", "the maintainer re-weigh of the singular-segment ruling", "pins BOTH halves as behaviour so this note cannot quietly stop being true" | card 9180 ruling; landing commit `53a48c93f4` | | `route-ledger.ts:545` object-less action note | 3913 | "the object-less spelling of the global-action call, routed rather than refused" | comment 5117039474 (`'global'` canonical; `/actions//:action` routes instead of 400-ing) | | `route-ledger.ts:549` activation note | 7526 | "(the live-mount parity gate, which asks the running router, caught it)" | ACCEPT 5251269251 | | `route-ledger.ts:578` apps note | 5040 E5b | "on a match runs the full chain: the policy keys ..., then target delegation" (the program's stage labels go with the citation) | card 5040 program table | | `route-ledger.ts:584` apps note | 5040 E7 | "LIVE since publish flipped from refusing to executing" | card 5040 comment 5176642864 (E7 flip) | ### Citation only (the sentence already stated the decision) - `route-ledger.ts`: 12038 at `:408`, `:411`, `:414`, `:417` (ruling 3A), `:421` (ruling 4A), `:424`, `:427`, `:500` (ruling 1C), `:503`, `:507` (ruling 2C, stage 2's twin wording); 9406 at `:405`; 10145 at `:435`, `:470`, `:472`; 3801 and 5561 at `:447`; 3656 at `:476`; 3718 at `:480` (x2); 7019 at `:497` (stage 2's twin wording); 7526 at `:537`; 12160 at `:548`. - `dispatcher-error-vocabulary.ts:437`, `:456` (5085). - `endpoint-executor.ts:232`, `:255` and `endpoint-policy.ts:263` (5040 E7, section 7-3, section 3.3): the hints and the warning already say publish rejects the form, that `script` and `proxy` wait for their own rulings, and that `cacheTtlSeconds` is GET-only. - `sandbox/body-runner.ts:391` (4352), `:469` (4345). - `standalone-stack.ts:152`, `:423` (3276). ## Text only, proven on the AST A skeleton of each changed file's TypeScript AST, with every maximal string expression (a literal, a template, or a `+` chain of them) collapsed to its list of embedded non-literal expressions, is identical before and after for all 14 changed files (BASE `5e5ce48cef` against `a67666a600`; the later merge of `origin/main` touches none of them). Re-wording or re-splitting literal text cannot move it; an identifier, operator, property name, embedded expression or statement change does. Control: changing `ACTIVATION_DENY_STATUS` to `ACTIVATION_DENY_STATUS + 1` in a copy of `activation-gate.ts` moves the hash. A literal can still hide a code or a route, so every changed string chain was also paired against its old twin and located: 48 chains changed, and each sits in prose: 28 `note:` values and 6 `why:` values, 2 `hint:` values, 1 `notice:`, 6 log-call messages, 1 `deps.error` message argument (the code argument is an identifier, untouched), 2 `new Error` messages and 2 message builders' `return` values, plus 4 test assertions. Control: flipping one `disposition: 'sdk'` to `'server-only'` in a copy is reported as `prop:disposition`. ## The ledger `node scripts/check-doc-authoring.mjs --census-ledger`, written to a scratch file first so its no-growth check reads the committed baseline, then installed: | | occurrences | (file, id) pairs | files | |---|--:|--:|--:| | `packages/runtime` before | 67 | 41 | 11 | | `packages/runtime` after | 0 | 0 | 0 | | whole ledger before | 550 | 375 | 147 | | whole ledger after | 483 | 334 | 136 | 63 lines deleted, 0 added; every other row is byte-identical. After merging `origin/main` (`e18fea6dcd`) the recomputed ledger is byte-identical to the committed one. `pnpm check:doc-authoring`: before "463 pinned site(s) across 147 file(s) ... no growth, no burn-down unrecorded", after "408 pinned site(s) across 136 file(s) ... no growth, no burn-down unrecorded". ## Pins, and that they can fail Three tests asserted an id. Each now asserts the words that carry the decision, and each was ablated through `scripts/ablation-replace.mjs` (anchor must hit, blob must change, restore proven by blob equal to HEAD and an empty `git diff HEAD`). The tests import the source by relative path, so no build sits between the mutation and the run. | Pin | Asserts now | Ablation (source text removed) | Result | |---|---|---|---| | `http-dispatcher.actions-doubled-redirect.test.ts:101` | "the interim precedence the console renderer applies" | that phrase | 1 failed / 13 passed | | `http-dispatcher.actions-doubled-redirect.test.ts:102` | "a pair the contract refuses rather than ranks" | that phrase | 1 failed / 13 passed | | `endpoint-executor.test.ts:212` | "rejected at publish pending their own rulings" | that phrase | 2 failed / 48 passed | | `sandbox/body-runner.test.ts:369` | "`body` only runs for `type: 'script'`" | "only runs for" | 5 failed / 31 passed | Restored, the three files run 3 passed (3), 100 tests passed. Three of the first ablation attempts were refused by the tool before any test ran (the replacement text was a substring of the anchor, so its count could not rise, and the file was restored); they were re-run with distinct markers, and only the re-runs are reported above. ## What ships Measured on the built `dist/index.js` and `dist/index.cjs`: every rewritten non-ledger sentence is present once in each, and none of the old spellings is. `ROUTE_LEDGER` and `UNREGISTERED_CODE_SITES` are absent from both: the route ledger and the vocabulary table are runtime-internal and do not ship. So the changeset, `@objectstack/runtime` `patch`, covers the refusal, error, warning and notice text, and the ledger and vocabulary notes ride along unpublished. ## Verification Heavy runs went through `scripts/pm/os-verify-lock.sh` (slot `issue-20752-s3`); each verdict line reads `VERDICT command-exit 0`. - Build: `turbo run build --filter=@objectstack/runtime...` 30/30 before the merge; after merging `origin/main` (`e18fea6dcd`), the whole workspace except docs, 72/72. - `@objectstack/runtime`, both vitest projects (`local` + `repo`): 305 files passed (305), 5080 passed / 11 skipped, at `7eca8ebff6` and again at the merged head `5a3cfda26d`. 305 is every `*.test.ts` in the package. - `@objectstack/runtime` typecheck (`tsc --noEmit` and `check:test-typecheck`): exit 0, before and after the merge. - Consumers that import the route ledger as source: `@objectstack/client` `client-url-conformance`, `route-ledger-coverage`, `route-ledger-response-schema`: 3 files, 9 tests passed; `@objectstack/dogfood` `route-ledger-live-mount-parity.dogfood.test.ts`: 1 file, 8 tests passed (after the full build). None of them reads a `note`; `route-ledger.conformance.test.ts` (in the runtime run) checks that every non-`sdk` row still carries one. - `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands` (no paths), derived at `a67666a600`: 75 commands, each run at the merged head `5a3cfda26d` from the worktree with its exit code recorded before any pipe, 75/75 exit 0. The dist-reading nine (`check:dts-closure`, `check:dual-build-cjs-loads`, `check:entry-guard`, `check:lean-entry-closure`, `check:published-files`, `check:sourcemap-no-sources-content`, `check:test-source-alias`, `check:type-check-coverage`, `check:type-check-debt`) ran after the full build. `--ran`, which recomputed the same 75 at `5a3cfda26d`: "75 derived famil(ies) accounted for — 75 run, 0 NOT-MEASURED (a DERIVED zero — all 75 recorded an exit code and none of them is 3)". - `check:dispatcher-error-vocabulary`: "OK — 55 unregistered code-stamping site(s), all classified". `check:issue-citations`: "no issue citations added against e18fea6 (11 file(s) read)". `check:nul-bytes`: OK, 9854 files. - `pnpm lint` (`eslint . --no-inline-config`, repo-wide, not narrowed) at `5a3cfda26d`: exit 0, 127 s. ## Acceptance notes - **Two cited cards live in objectui and could not be opened here.** `objectui#6593` and `objectui#5933` answer 403 to this session (the repository is not enabled for it). Their decisions were read through the records in this repository that cite them: `DuplicatePackageResponseSchema`'s docblock and the client's `duplicate` comment (6593: a console that read the envelope `success` reported a partial or empty duplicate as done), and card 11519's body, ruling 5393507405 and the spec refusal message in `ui/action.zod.ts` (5933: the console renderer's interim declared-wins precedence). The rewritten sentences say no more than those records do. - **Comments keep their numbers.** Only strings are in the ledger; the `//` and docblock citations in these files are the sanctioned home for an id and are untouched. - **The checklist still reads true.** `docs/qa/platform-checklist/areas/access-security.json:2195` and `api-backend.json:1567` quote "functionally equivalent to deleting it" from the enablement refusal; that phrase is kept, so neither clause changes truth. - **Out of this stage's files:** `packages/lint/src/validate-action-body-writes.test.ts:316` pins `#4345` in the lint rule's own message. That string is in the ledger's `packages/lint` row, not this stage's; it rides whichever stage takes that row. Noted, not filed. - **Stages left on the card:** 4 (`verify`, `plugin-dev`, `plugin-hono-server`, 14 occurrences) and 5 (`qa`, 102). --- _Generated by [Claude Code](https://claude.ai/code/session_01VvcEokUG1tvVxkceYfR5XB)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent e47355b commit 7c5a311

16 files changed

Lines changed: 99 additions & 128 deletions
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
---
2+
'@objectstack/runtime': patch
3+
---
4+
5+
Runtime refusals, boot errors and warnings no longer cite tracker numbers; each one states the decision behind it in words
6+
7+
Clause-②: no
8+
9+
Strings `@objectstack/runtime` shows to callers, authors and operators pointed at an issue-tracker number for the reason behind them. The number goes; where the sentence did not already say what was decided, it now does.
10+
11+
- The enablement refusal (`POST /actions/_activation/:object/:action`) adds that the switch is not scoped to the caller's organization, which is why `manage_metadata` gates it.
12+
- The doubled post-success navigation warning (`[action-contract]`) says the contract refuses a pair of destinations rather than ranking them, and that "declared `onSuccess` wins" is the console renderer's interim precedence, not a contract.
13+
- The legacy database notice says `dev`, `start` and `migrate` now share one default database file.
14+
- The `BodyRunner` warning for a `log` capability with no logger says the capability writes only to the factory's logger, never to `console`.
15+
- The seed tenancy handoff warning says what a failure leaves behind: seed and API writes on separate autonumber counters until the next boot's migration repairs it.
16+
- The auth forwarder's sanitised-500 log line says the client's message was withheld unconditionally and that this line is where the original error is read.
17+
- The `StandaloneStack` guard for a driver kind with no dispatch arm says falling through to SQLite would hand the caller an engine they never selected.
18+
- The `StandaloneStack` refusals for an unsupported or URL-less database driver, the declarative-endpoint hints, the `cacheTtlSeconds` warning and the two other `BodyRunner` warnings drop their citations; each already said what it refuses and why.
19+
20+
Text only: no status, error code, field, route, export or control flow moves. A log filter or test that matched the old text (for example a `See #NNNN` suffix) needs the new spelling.

‎packages/runtime/src/action-execution.ts‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2472,9 +2472,10 @@ export function doubledPostSuccessNavigationWarning(
24722472
const where = objectName ? `${objectName}/${actionDef?.name ?? '<unnamed>'}` : String(actionDef?.name ?? '<unnamed>');
24732473
return (
24742474
`[action-contract] Action '${where}': the handler returned \`redirectUrl\` while the action `
2475-
+ 'also declares `onSuccess.navigate` — two post-success destinations for one success '
2476-
+ '(#11519). The DECLARED `onSuccess` wins and the handler\'s `redirectUrl` is ignored '
2477-
+ '(interim renderer precedence, objectui#5933). Fix the action, not the renderer: keep '
2475+
+ 'also declares `onSuccess.navigate` — two post-success destinations for one success, '
2476+
+ 'a pair the contract refuses rather than ranks. The DECLARED `onSuccess` wins and the '
2477+
+ 'handler\'s `redirectUrl` is ignored (the interim precedence the console renderer '
2478+
+ 'applies, which no contract promises). Fix the action, not the renderer: keep '
24782479
+ '`onSuccess` and stop returning `redirectUrl` from the handler, or drop `onSuccess` and '
24792480
+ 'let the handler return drive the navigation. There is no `precedence` field, by ruling.'
24802481
);

‎packages/runtime/src/app-plugin.ts‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1777,7 +1777,11 @@ export class AppPlugin implements Plugin {
17771777
// organization was just created and that must stand whatever
17781778
// happens here. `warn` and not `error` — nothing was lost, and the
17791779
// `kernel:ready` migration retries the same repair on next boot.
1780-
ctx.logger.warn('[AppPlugin] seed tenancy handoff failed (#8686)', {
1780+
ctx.logger.warn(
1781+
'[AppPlugin] seed tenancy handoff failed: the seed rows were not stamped with the new '
1782+
+ 'organization, so seed and API writes stay on separate autonumber counters until the '
1783+
+ 'next boot\'s migration repairs it',
1784+
{
17811785
error: e?.message ?? String(e),
17821786
});
17831787
}

‎packages/runtime/src/dispatcher-error-vocabulary.ts‎

Lines changed: 19 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -346,14 +346,15 @@ export const UNREGISTERED_CODE_SITES: readonly UnregisteredCodeSite[] = [
346346
why:
347347
"Three approvals route factories (`decisionRoute`, `flowMoveRoute`, `threadRoute`) spell the " +
348348
'terminal 500 catch\'s code as a template — `` `APPROVAL_${action.toUpperCase()}_FAILED` `` and ' +
349-
'two siblings — so the family, not a literal, is what exists in source. #8885 registered all ' +
350-
'nine codes the family produces, and its pin is what keeps that true: it enumerates the ' +
349+
'two siblings — so the family, not a literal, is what exists in source. All nine codes the ' +
350+
'family produces are registered in the ledger, and this row\'s pin is what keeps that true: it enumerates the ' +
351351
'registered `POST /approvals/requests/:id/<action>` routes and asserts the code each catch arm ' +
352352
"would generate parses against ApiErrorSchema's closed union, mirroring the production " +
353353
"template exactly (single-occurrence `.replace('-', '_')` included). So a tenth action route " +
354354
'whose generated code nobody registers fails THERE, mechanically. This row records that ' +
355-
'division of labour instead of letting the scan imply it checked something it cannot: #9223 ' +
356-
'widened the scan enough to SEE the template, and seeing it is what makes the pin an ' +
355+
'division of labour instead of letting the scan imply it checked something it cannot: the scan ' +
356+
'reports a template-spelled code under its family identity rather than dropping it, so it ' +
357+
'SEES the template, and seeing it is what makes the pin an ' +
357358
'accounted-for half rather than a local habit in one package.',
358359
},
359360

@@ -433,8 +434,8 @@ export const UNREGISTERED_CODE_SITES: readonly UnregisteredCodeSite[] = [
433434
why:
434435
"better-auth's own `APIError` vocabulary, and it cannot reach this door: `domains/auth.ts` " +
435436
'catches everything the auth service throws and answers `deps.error(INTERNAL_ERROR_MESSAGE, 500)` ' +
436-
'— the message withheld UNCONDITIONALLY and the code status-derived, never `errorFromThrown` ' +
437-
'(#5085). better-auth answers its own failures with a `Response` rather than by throwing, and ' +
437+
'— the message withheld UNCONDITIONALLY and the code status-derived, never `errorFromThrown`. ' +
438+
'better-auth answers its own failures with a `Response` rather than by throwing, and ' +
438439
'that body is returned untouched as `result`. So the string never lands in an ADR-0112 ' +
439440
'`error.code`. This is the row that shows why verdicts are DECLARED: it is written exactly ' +
440441
'like FLOW_FAILED and a documented catch one layer up makes it unreachable.',
@@ -452,7 +453,7 @@ export const UNREGISTERED_CODE_SITES: readonly UnregisteredCodeSite[] = [
452453
'`remove-member-permission-guard.ts`; only the envelope differs). It cannot reach this door, ' +
453454
'by the same route the IMPERSONATION_ROTATION_FAILED row documents and re-verified here: ' +
454455
'`domains/auth.ts` catches everything the auth service throws and answers ' +
455-
'`deps.error(INTERNAL_ERROR_MESSAGE, 500)` — unconditionally, never `errorFromThrown` (#5085). ' +
456+
'`deps.error(INTERNAL_ERROR_MESSAGE, 500)` — unconditionally, never `errorFromThrown`. ' +
456457
'So the string never lands in an ADR-0112 `error.code`.',
457458
},
458459
// ── [#10352] better-auth's OWN vocabulary, now restamped in-repo ───────
@@ -516,7 +517,8 @@ export const UNREGISTERED_CODE_SITES: readonly UnregisteredCodeSite[] = [
516517
why:
517518
'The target-side twin of the row above, from the same better-auth 1.7.1 file ' +
518519
"(`dist/plugins/admin/error-codes`), likewise read off `plugin.$ERROR_CODES` and raised " +
519-
"`APIError.from('FORBIDDEN', cannotImpersonateAdmins)`. #9968 makes it reachable for the " +
520+
"`APIError.from('FORBIDDEN', cannotImpersonateAdmins)`. The in-repo re-implementation of the " +
521+
"vendor's impersonation handler, which admits an ADR-0068 platform admin, makes it reachable for the " +
520522
"first time — the vendor gated it on the legacy `user.role` scalar nothing writes post " +
521523
"ADR-0068 D2, so the vendor's own promise was inert — but reachable in the vendor's wire " +
522524
"shape under the vendor's spelling, which changes nothing about whose vocabulary it is.",
@@ -592,7 +594,8 @@ export const UNREGISTERED_CODE_SITES: readonly UnregisteredCodeSite[] = [
592594
why:
593595
'The same ADR-0087 conversion-notice vocabulary as the apply.ts row above, met at a TYPE ' +
594596
'position: `ArtifactConversionNotice.code` is the literal in a structural mirror of ' +
595-
"ConversionNotice, declared so the artifact-ingestion forward-conversion policy (#12772) " +
597+
"ConversionNotice, declared so the artifact-ingestion forward-conversion policy — which runs the " +
598+
'ADR-0087 conversions over an artifact built by older tooling before its strict parse — ' +
596599
'keeps the spec ROOT import out of its public declaration surface (the root reference made ' +
597600
"every downstream type program load the 2MB root twice and pushed a TEST_DEBT re-measure " +
598601
"over CI's tsc heap ceiling). A literal type stamps nothing at runtime — notices flow to an " +
@@ -674,7 +677,7 @@ export const UNREGISTERED_CODE_SITES: readonly UnregisteredCodeSite[] = [
674677
why:
675678
'The ADR-0090 D7 / ADR-0086 D1 refusal: an environment overlay may only TIGHTEN a packaged ' +
676679
"object's OWD. The token reaches the wire verbatim but NOT in `code` — it rides the wire in " +
677-
'TWO fields since #9232 narrowed the flat REST door like every other: the 403 body carries the ' +
680+
'TWO fields because the flat REST door narrows like every other door: the 403 body carries the ' +
678681
'closed member the status derives in `code` (`PERMISSION_DENIED`) and this string, unchanged, ' +
679682
'in the open `declaredCode` sibling beside it. `packages/rest/src/meta-object-owd-gate.test.ts` ' +
680683
'drives `PUT /api/v1/meta/object/:name` and asserts BOTH fields on the refusal body. So the ' +
@@ -684,13 +687,16 @@ export const UNREGISTERED_CODE_SITES: readonly UnregisteredCodeSite[] = [
684687
'already names: the body parses, and what an unswept producer loses instead is its semantic ' +
685688
'code, silently demoted off `error.code` until registered. Which is exactly what a ' +
686689
'`pending-registration` row records, and registering the code is still what ratchets it out. ' +
687-
'[#9460] Invisible to BOTH vocabulary gates until now, and not for its casing: the file throws ' +
690+
'Invisible to BOTH vocabulary gates until the scan learned the code-carrying helper shape, and not for ' +
691+
'its casing: the file throws ' +
688692
'through a code-carrying helper (`postureError(code, message)`), so the stamp `(err as any).code ' +
689693
'= code` knows the token `code` but not the value, while the call site knows the value and never ' +
690694
'writes the token. Every pattern in this gate and in `check:error-code-casing` anchors on that ' +
691695
'token, so both read the file and both reported nothing. ⚠️ The spelling is LOWERCASE, so ' +
692-
'ADR-0112 D1 forbids registering it as spelled — the rename-or-keep-the-#9106-demote call is ' +
693-
"the `packages/spec` lane's, tracked as #9460 half (2) and NOT decided here. The row records " +
696+
'ADR-0112 D1 forbids registering it as spelled — the call between renaming it and keeping the ' +
697+
'demote (the closed member in `code`, this spelling in `declaredCode`) is ' +
698+
"the `packages/spec` lane's; until that lane registers a code the standing demote answers " +
699+
'this spelling, and the call is NOT decided here. The row records ' +
694700
'that a live wire code is outside the vocabulary; it does not prescribe the remedy.',
695701
},
696702

‎packages/runtime/src/domains/activation-gate.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -275,8 +275,8 @@ export function refuseUngrantedActivationAuthoring(
275275
handled: true,
276276
response: deps.error(
277277
`Enabling or disabling ${artifact.subject} requires the \`${ACTIVATION_AUTHORING_CAPABILITY}\` capability — ` +
278-
`switching a shipped artifact off is functionally equivalent to deleting it for as long as it stays off ` +
279-
`(#10243).`,
278+
`switching a shipped artifact off is functionally equivalent to deleting it for as long as it stays off, ` +
279+
`and the switch is not scoped to the caller's organization.`,
280280
ACTIVATION_DENY_STATUS,
281281
{ code: ACTIVATION_DENY_CODE },
282282
),

‎packages/runtime/src/domains/auth.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,8 @@ export async function handleAuthRequest(deps: DomainHandlerDeps, _path: string,
140140
const logger = deps.logger ?? console;
141141
logger?.error?.(
142142
'[auth] the auth service threw while handling the request; the client was answered '
143-
+ 'with a sanitised 500 (#5085)',
143+
+ 'with a sanitised 500: the message is withheld unconditionally, and this line is where the '
144+
+ 'original error is read',
144145
err instanceof Error ? err : new Error(String(err)),
145146
);
146147
return { handled: true, response: deps.error(INTERNAL_ERROR_MESSAGE, 500) };

‎packages/runtime/src/endpoint-executor.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ describe('planEndpointTarget — the unsupported subset is enumerated once', ()
209209
// The reason names the type so an author is not left guessing which of
210210
// their endpoints the runtime declined.
211211
expect((plan as any).reason).toContain(`'${type}'`);
212-
expect((plan as any).hint).toContain('#5040 §7-3');
212+
expect((plan as any).hint).toContain('rejected at publish pending their own rulings');
213213
});
214214
});
215215

‎packages/runtime/src/endpoint-executor.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ export function planEndpointTarget(endpoint: ApiEndpoint): EndpointTargetPlan {
229229
`Endpoint '${endpoint.name}' declares type 'object_operation' but `
230230
+ `objectParams.${!object ? 'object' : 'operation'} is missing.`,
231231
hint: 'An object_operation endpoint must declare both `objectParams.object` and '
232-
+ '`objectParams.operation`; publish rejects the incomplete form (#5040 E7).',
232+
+ '`objectParams.operation`; publish rejects the incomplete form.',
233233
};
234234
}
235235
return { kind: 'object_operation', object, operation };
@@ -252,7 +252,7 @@ export function planEndpointTarget(endpoint: ApiEndpoint): EndpointTargetPlan {
252252
reason: `Endpoint '${endpoint.name}' declares type '${endpoint.type}', which this runtime does not execute.`,
253253
hint: "Only 'object_operation' and 'flow' endpoints execute in 17.x. 'script' and 'proxy' "
254254
+ 'are rejected at publish pending their own rulings — script reachability through the '
255-
+ 'automation service is unverified, and proxy is an outbound (SSRF) surface (#5040 §7-3).',
255+
+ 'automation service is unverified, and proxy is an outbound (SSRF) surface.',
256256
};
257257
}
258258

‎packages/runtime/src/endpoint-policy.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ export function computeCacheControl(
260260
if (method.toUpperCase() !== 'GET') {
261261
logger?.warn?.(
262262
`[dispatcher] endpoint '${endpoint.name}' declares \`cacheTtlSeconds\` on a ${method.toUpperCase()} endpoint. `
263-
+ '`cacheTtlSeconds` is GET-only (#5040 §3.3) and no Cache-Control header will be sent. Remove the key, or '
263+
+ '`cacheTtlSeconds` is GET-only and no Cache-Control header will be sent. Remove the key, or '
264264
+ 'declare the endpoint as GET.',
265265
);
266266
return undefined;

‎packages/runtime/src/http-dispatcher.actions-doubled-redirect.test.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ describe('REST /actions — doubled post-success navigation diagnostic (#11519)'
9898
expect(doubled[0]).toContain("'crm_lead/open_portal'");
9999
expect(doubled[0]).toContain('onSuccess');
100100
expect(doubled[0]).toContain('redirectUrl');
101-
expect(doubled[0]).toContain('objectui#5933');
102-
expect(doubled[0]).toContain('#11519');
101+
expect(doubled[0]).toContain('the interim precedence the console renderer applies');
102+
expect(doubled[0]).toContain('a pair the contract refuses rather than ranks');
103103
});
104104

105105
it('does NOT alter the wire — the handler return value still reaches the client intact', async () => {

0 commit comments

Comments
 (0)