Skip to content

docs(rest-test): three production seams plus one test-only injection, not four (#12537) - #12646

Merged
os-litant merged 1 commit into
mainfrom
claude/issue-12537-exec-ctx-seam-census
Aug 27, 2026
Merged

docs(rest-test): three production seams plus one test-only injection, not four (#12537)#12646
os-litant merged 1 commit into
mainfrom
claude/issue-12537-exec-ctx-seam-census

Conversation

@os-litant

@os-litantos-litant commented Aug 27, 2026

Copy link
Copy Markdown
Collaborator

Part of #12537

packages/rest/src/package-door-declared-code.test.ts describes its SITES
table as the /api/v1/packages direct-mount door's "four seams". Three of the
four are production producers. The fourth — resolveExecutionContext — is a
test-only injection point: no production throw of any kind can reach
sendThrownError through it.

This PR corrects the record in that one file. It is comment/prose only — no
test removed, no assertion touched, no behaviour changed.

What I measured

Read on origin/main @ aa5994e17, from the composition rather than inferred
from the card:

sitemeasurement
rest-api-plugin.ts:471the only production supplier of this option, repo-wide: resolveExecutionContext: (req) => restServer.resolvePackageRouteExecutionContext(req)
rest-server.ts:1481resolvePackageRouteExecutionContext(req: any), declared to return a promise of context-or-undefined — and not async. Whole body: an optional-chained req?.params?.environmentId read, then return this.resolveExecCtx(environmentId, req).catch(() => undefined)
rest-server.ts:1453private async resolveExecCtx(...) — being async, calling it cannot throw synchronously; it always returns a promise
package-routes.ts:81the consumer then awaits options.resolveExecutionContext(req).catch(() => undefined) — a second swallow

⇒ A production resolver delivers exactly two things: a context, or undefined.
Its rejections are swallowed twice and land on the anonymous-deny floor as a
401 — they never reach sendThrownError. The only route from this seam to
sendThrownError is a synchronous throw (it happens before .catch is
attached, so it rejects refusePackageRequest itself and the handler's try
catches it) — and the production wrapper above has no statement that can make
one.

⚠️A correction to the card's stated reason. The card compresses this to
"private async behind .catch(() => undefined)". Those are two guards doing
two different jobs: resolveExecCtx being async denies the sync limb, and
the .catch() denies the rejection limb. Because the wrapper
resolvePackageRouteExecutionContext is notasync, the sync question is
genuinely live at that seam and had to be measured rather than assumed. Same
conclusion, different argument.

⚠️ Nor can an embedder reach it: registerPackageRoutes and
PackageRoutesOptions are not exported from packages/rest/src/index.ts
(the package publishes a single . entry, and direct-mount-composition.ts is
their only importer). That zero was reverse-checked against terms known present
in the same file — export (17 hits) and RestServer — never a substring of
the term under test.

Reverse-check: the other three ARE production-reachable

Not assumed. publish / get / delete are PackageService methods resolved
from the service registry per request (#7563) and awaited inside each handler's
try, so a rejection reaches sendThrownError. The live implementation is
packages/services/service-package/src/index.ts, which throws (throw error
for declaresHttpAnswer, throw packageSeamUnreadableError()) and carries its
own publish-driver-fault.test.ts / delete-driver-fault.test.ts. Section 5 of
this suite already drives that path end-to-end with a real ObjectQL, a real
protocol and a failing driver.

The tell was already in the file, unremarked: the three production seams inject
async () => { throw error; } — a rejection. The resolver seam injects a
non-async() => { throw error; } — a sync throw. It has to, because a
rejection would be swallowed at package-routes.ts:81 and answer 401 instead.

The census, re-derived

Six sites corrected. I did not inherit the dispatch's list.

line (base)what it saiddisposition
:55"Section 1 drives all four seams through the real registrar"true of the suite, but sat under "The channel is live at this door … the door forwards whatever they throw" — qualified
:61"every status-declaring coded throw reachable at these four seams"reachability claim — corrected to the three production seams
:197-198"Same four seams as package-routes-coded-error-mapping.test.ts"true of the table — qualifier added
:262// 1. The demote reaches the wire, at every seam this door has⭐ a census claim spelled without the word "four"corrected
:481"No producer reaching these four seams can put a driver errno in declaredCode"reachability claim — corrected
:505the falsifier's third limb⭐ the sharp one — retired, with its reason

Counted and deliberately left as-is:

  • :87 — "six shapes, four of which demote". Not about seams at all.
  • :91-92 — "package-routes-coded-error-mapping.test.ts contributed 4 of the
    30 … at the four seams". This is a record of a past ablation measurement,
    and its "four seams" refers to that other file's four test seams, which
    really are four. True as written, and measured numbers are not rewritten to
    fit a later census.

The sharp site: a falsifier that cannot be reached

:505-511 states a falsifier "so the next reader inherits a measurement instead
of an argument", and lists three ways to reach it. The third was "or a
resolveExecutionContext that throws one synchronously". Under the measurement
above that limb is unreachable by construction, not merely unobserved — so
it was not a falsifier, and a reader who tried it would have spent the time to
discover it cannot happen.

It is retired rather than reworded, and the block now says so and says why,
with a note that if the swallow at rest-server.ts:1483 is ever un-done the limb
becomes real again and belongs back on the list. The other two limbs are
untouched: both are genuinely reachable.

The resolveExecutionContext test case is KEPT

⛔ Not deleted. It still pins something real — how this door answers when an
injected resolver throws synchronously — and reached() keeps it from going
vacuous. It is simply not evidence about a production path, so it is labelled
as a test-only injection point and nothing in the file cites it as one. Deleting
a test to make a census true would be the census lying in the other direction.

The reason is stated once, in a Seam census block in the module docblock,
and cited from the five other sites — not restated at each.

ℹ️ One judgement call worth flagging: the site's name string
('GET /packages — the capability gate resolver throws') is unchanged,
because a string literal is code and changing it would break the comment-only
proof below. It is accurate as it stands. If you would rather the test output
also carry the "test-only" marker, that is a one-line follow-up.

Comment-only, proven with a calibrated instrument

Both blobs transpiled with removeComments: true (typescript 6.0.3), emitted
program hashed:

base sha256: 8849a4b2c9f4c1972b218a7f1fd81f4a48b29439bf08e77580b27b2896a1473a
head sha256: 8849a4b2c9f4c1972b218a7f1fd81f4a48b29439bf08e77580b27b2896a1473a
EQUAL (comment-only): true

An equality from an uncalibrated instrument is not evidence, so the instrument
was reverse-checked in both directions against the same head blob:

A) code-token mutant 080c09894cdbd754cded21c148ce210905490ed2e5262458a5562dbc05e35a49 DIFFERS: true
B) comment-token mutant 8849a4b2c9f4c1972b218a7f1fd81f4a48b29439bf08e77580b27b2896a1473a SAME: true
VERDICT: PROVEN comment-only, instrument calibrated both ways

A code token moves the hash; a comment token does not. git diff --stat:
1 file changed, 91 insertions(+), 14 deletions(-).

Tests and gates

All heavy runs serialized through scripts/pm/os-verify-lock.sh (slot
issue-12537). Every verdict quoted below is the tool's own printed line,
never a $? read through a pipe.

Build — the dependency closure a fresh worktree needs before its tests mean
anything: pnpm --filter '@objectstack/rest^...' build
os-verify-lock: VERDICT command-exit 0 · held the lock 310s (5m10s) · waited 0s

Suitepnpm --filter @objectstack/rest exec vitest run --maxWorkers=2 src/package-door-declared-code.test.ts src/package-routes-coded-error-mapping.test.ts

Test Files 2 passed (2)
Tests 105 passed (105)

os-verify-lock: VERDICT command-exit 0 · held the lock 11s · waited 1s

The edited file on its own: Test Files 1 passed (1) / Tests 58 passed (58).

⚠️Two numbers I was handed disagree with what I measured — reported, not
reconciled.
This file carries 58 cases, not 155, and 5 numbered
sections (banners 1.5. at :328, :392, :459, :525, :625), not 4.
The 155 traces to this file's own docblock at :137"Measured: 30 failed /
125 passed of 155"
— which is the total of a multi-file ablation run (the
same paragraph records that package-routes-coded-error-mapping.test.ts
contributed 4 of those 30), not this file's case count. ⛔ That prose is
untouched: it is a correct record of a past measurement, and measured numbers do
not get rewritten to fit a later census.

Gate families — derived with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, which
took the change set from git itself (it reported 1 path: this file). Each ran to
its own verdict line:

gateits own verdict line
check:nul-bytesOK (scanned 6987 text file(s) … no raw ASCII control bytes)
scripts/check-comment-mask-adoption.mjsOK — 20 private comment-stripper(s) … all 20 recorded and every recorded row still reached
check:cross-package-test-inputsOK: 20 package(s) read outside themselves, all declared
check:dispatcher-error-vocabularyexit 0 — door typing checked for packages/rest/src/error-response.ts
check:test-source-aliasOK — 72 packages with tests scanned
check:engine-double-contract632 (file, verb) row(s) held by the RETAINED ledger
check:where-matcher0 silently-wrong and 0 unjudged matcher(s) … none new
check:objectql-double-limit… none new
@objectstack/resttypecheckexit 0

os-verify-lock: VERDICT command-exit 0 · held the lock 107s (1m47s) · waited 0s

Re-run on the final commit. The gate batch above ran on the working tree, so
the ratchet families were re-run after the last commit and are quoted from that
run — a green measured on a tree that is no longer HEAD is not a green:

RATCHET RUN AT HEAD=d496a4301 (worktree clean: 0 path(s) dirty)
where-matcher EXIT=0
objectql-double-limit EXIT=0
engine-double-contract EXIT=0
nul-bytes EXIT=0
os-verify-lock: VERDICT command-exit 0 · held the lock 58s · waited 0s

⚠️Typecheck coverage, stated precisely rather than implied. The package's
build program does not read this file: tsc --noEmit --listFiles returns
0 hits for package-door-declared-code.test.ts. The half that does read it
is check:test-typecheck (tsconfig.test.json), where --listFiles returns
1 hit, and it passed:
check:test-typecheck: OK — @objectstack/rest's test layer compiles under packages/rest/tsconfig.test.json.
So "typecheck is green" is a claim about the test program here, not about the
build program.

Narrowing, declared: I ran the targeted families above plus the two affected
suites — not the full 176-family farm, and not a repo-wide pnpm lint. CI runs
those on every pull request regardless, and the full-farm result is CI's to
report.

No changeset: packages/rest publishes ["dist", "README.md", "CHANGELOG.md"],
the edit is comments inside a src/** test file that dist does not carry, and
the emitted-program hash above shows nothing published can differ. Labelled
skip-changeset.

Scope

⛔ Nothing outside packages/rest/src/package-door-declared-code.test.ts was
edited. packages/rest/src/rest-server.ts was read only — it is held by
PR #12421.

Part of #12537, not a closing keyword: the other half of that card — whether
.catch(() => undefined) at rest-server.ts:1483 should swallow a door's
rejections at all — is untouched here and stays open. It is not a question
this diff can answer: it would change what a public door emits, and the file is
fenced. I checked the conditional the claim raised, and it does not fire —
the swallow is documented at neither site (rest-server.ts:1483 nor
package-routes.ts:81; the nearby docblocks explain why the wrapper exists and
what an absent resolver means, never what a failing one means). So
"deliberate" is not established by the code as it stands, and a closing keyword
would be wrong.


Generated by Claude Code

… not four (#12537)
The `/api/v1/packages` direct-mount door's suite described its `SITES` table as
the door's "four seams". Three of the four are production producers; the fourth,
`resolveExecutionContext`, is a test-only injection point. A production resolver
cannot throw synchronously — `resolvePackageRouteExecutionContext` is a
non-`async` wrapper whose only call is to a `private async` method — and its
rejections are swallowed twice, at `rest-server.ts:1483` and again at
`package-routes.ts:81`, landing on the 401 anonymous-deny floor instead of
`sendThrownError`.
Corrects six sites, states the reason once in a `Seam census` block and cites it
from the others, and retires the falsifier limb that named a synchronously
throwing `resolveExecutionContext`: unreachable by construction, so not a
falsifier. The `resolveExecutionContext` test case itself is KEPT and
relabelled, never deleted — it still pins how the door answers an injected
resolver that throws.
Comment/prose only: both blobs transpile to a byte-identical program under
`removeComments`, with the instrument reverse-checked in both directions (a code
token moves the hash, a comment token does not).
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UjujZN219uFzBhSYfMykCd
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 0 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json c4f3263d903a446d1c612ff5cfd88988b9812d17packageMentionDocs.

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-litant@claude