docs(rest): replace the slot-lookup pin's false "no tsc program compiles this" premise with the measured reason - #17714
Conversation
…les this" premise with the measured reason
`rest-api-plugin-slot-lookups.test.ts`'s docblock justified its runtime pin by
asserting that `packages/rest` "declares no `typecheck` script (it is a
DEBT/TEST_DEBT ledger entry), so NO tsc program compiles this file". Measured on
this tree, all three halves are false:
- `@objectstack/rest`'s `typecheck` is `tsc --noEmit && pnpm
check:test-typecheck`, whose second half runs `--project tsconfig.test.json`;
- the `DEBT` object literal in `scripts/check-type-check-coverage.mjs` holds 4
keys and `TEST_DEBT` holds 1 (`@objectstack/http-conformance`); neither names
`@objectstack/rest`;
- `tsc --listFiles -p tsconfig.test.json` puts this file in a 742-file program
(firing controls: `rest-api-plugin.ts` and `rest.test.ts` both present;
nonsense control: absent), while the build program `tsconfig.json` carries
558 files and zero `.test.ts`.
So the "a type-level pin here would be a phantom" ground is dead: a
`@ts-expect-error` written here is live. The docblock now states the reason that
actually holds, derived from this file's own subject — the property is not
expressible as a type, because the providers are positional parameters sharing
one identical type and the slot name each resolves is a bare `string` literal
handed to `getService<T>(name: string): T`, so "this argument resolves that slot"
is a value-level identity only invocation can observe.
Docblock only: no pin moved, no directive deleted, no case body touched.
Claude-Session: https://claude.ai/code/session_01TSf4DV7ziu4V5j73e46b7c
Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift CheckNothing 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
Coarse fallback — 0 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): |
Review of record — ACCEPTHead reviewed: Reviewed-by:
Gates on this head, exit codes captured before any pipeThe control returns the opposite verdict on the same predicate, so the gate is discriminating rather than green-by-default. CI
The diff is provably docblock-only, not merely described as suchParsed from the diff rather than taken on trust: 28 added lines, 7 removed, 0 non-comment lines in either direction, across 1 file. No pin moved, no directive deleted, no case body touched — which is what the card's fence required. The two false clauses, re-measured by this seat⛔ Both ledgers were re-derived inside each object literal, not by a whole-file grep — the card records that such a count is not evidence, and it is right. The replacement reason, corroborated at TWO independent placesThe new prose rests on ⇒
Its two out-of-scope findings are FILED, not absorbedThe card was fenced to one docblock and told to report rather than widen. It reported, and both are now cards:
⭐ And it answered the question triage asked by name — "say, in one sentence, whether a sixth carrier exists" — with six, found by a scan keyed on the claim rather than a phrase, controlled by a positive leg that shares the target's failure mode (a phrase known present and wrapped across a line with a comment prefix inside the wrap) against a plain VerdictACCEPT. One file, comment bytes only, provably so; every factual claim measured, by the dev and again here; the replacement reason derived from this file's own subject rather than copied from #16742; the fence honoured in both directions — nothing widened, everything found reported. Landing armed by this seat. Generated by Claude Code |
Fixes #17304
packages/rest/src/rest-api-plugin-slot-lookups.test.ts's docblock justified its runtime pin with a three-part premise. Two of the three parts, and the conclusion drawn from them, are false on this tree. This replaces them with the reason that actually holds.Docblock only — no pin moved, no directive deleted, no case body touched. +28/-7 in one file.
Measurement 1 — the premise, clause by clause
The two ledger key sets were re-derived from the object literals themselves, by parsing
scripts/check-type-check-coverage.mjswith the TypeScript compiler's own parser — not by a whole-file grep, whose count the card records as not evidence. Parser positive control: the same parse must report@objectstack/honoinsideDEBT(it does) and a non-emptyTEST_DEBT(it does).packages/rest/tsconfig.jsonexcludes its.test.tsfilesexcludenames both test spellings; the build program carries 558 files and 0srctest filestypecheckscript"typecheck": "tsc --noEmit && pnpm check:test-typecheck"DEBTliteral = 4 keys (cloud-connection,hono,observability,spec-monorepo);TEST_DEBTliteral = 1 key (http-conformance).@objectstack/restis in neitherpnpm check:type-check-coverageagrees independently: "76/80 workspace packages type-checked … 4 in the DEBT ledger … 1 package(s) still hide their own tests from tsc".Measurement 2 — does a tsc program actually compile this file
The question the card left open.
tsc --noEmit --listFiles --pretty falseover each of the package's two real programs, with both controls; each exit code captured before any pipe (redirect first,EXIT=$?second):tsconfig.test.jsonprogramtsconfig.json(build) programrest-api-plugin-slot-lookups.test.tsrest-api-plugin.ts(production source)rest.test.ts(test layer)srctest files in programThe firing control shares the zero's failure mode: same directory, same
--listFilesoutput format, same path spelling. So the build program's0is a real absence, and the test program's1is real membership — the target is line 538 of that program's 742.That program is not merely on disk, it is invoked:
pnpm --filter @objectstack/rest typecheckexits 0 and its second half prints⇒ the "phantom" ground is dead. A
@ts-expect-errorwritten in this file is live and reports TS2578 the moment it suppresses nothing.The real reason, derived from this file's own subject
The pin stays a runtime pin, for a reason that survives any script list: the property is not expressible as a type.
RestServerreceives the providers as positional parameters declared with one identical type — a function taking an optional environment id and returning a promise ofanyorundefined. Every permutation of those parameters is therefore assignable, and no assertion over that signature can go red when the wiring is wrong.PluginContext.getService, whosenameparameter is a barestringand whose result type is supplied by the caller. Neither the argument type nor the result type carries evidence of which slot was read.init/startactually makes.Nothing from another card's reason was copied in; this is derived from
rest-server.ts's constructor andpackages/core/src/types.ts'sgetServicedeclaration.Measurement 3 — is there a SIXTH carrier
One sentence, as triage asked: yes — at least six more carriers of this same claim exist beyond the fifth, in three further wordings, the clearest being
packages/lint/src/runtime-gate.derived-context-keys.test.ts, which asserts "this package'stsconfig.jsonexcludes the test spelling, so no tsc program compiles this file" while@objectstack/lint's owntypechecknames atsconfig.test.jsonwhoseincludeis the whole ofsrc.They are reported, not touched — the fence on this card is one docblock, and an unreviewed widening is exactly what #17304 exists instead of.
How the scan was keyed and controlled: the family's sentences defeat a plain grep by construction, so the scan normalises each tracked text file — strips block/line comment continuation prefixes, strips backticks and quote characters, joins the file into one whitespace-collapsed string — and then matches on the claim (a package or directory is not compiled / has no typecheck script / is a ledger entry), not on a phrase.
git grep -FThe positive control shares the target's failure mode rather than only its channel, which is the point: a control proving the path is merely readable would have passed over every wrapped sentence in the family.
Stage 2 narrowed the 214 claim-shaped files to 33 package source files and each was read against the truth for the package it is about. Carriers found, all with the same shape (a live justification resting on a claim that is false on this tree):
packages/lint/src/runtime-gate.derived-context-keys.test.tstsconfig.jsonalone — names neither atypecheckscript nor a ledgertypecheckrunscheck:test-typecheck --project tsconfig.test.json,include= all ofsrcpackages/cli/src/utils/format.exit-code.test.tspackages/cli/test/)packages/cli/tsconfig.test.json'sincludeis exactlytestplus three vitest configs, andtypechecknames itpackages/cli/src/commands/validate-json-strict-exit.e2e.test.tspackages/plugins/plugin-sharing/src/exec-context-annotation.pin.tsTEST_DEBTholds one key and it is not this package;tsconfig.test.jsonincludes all ofsrcpackages/plugins/plugin-sharing/src/logger-required-warn.pin.tspackages/plugins/plugin-approvals/src/exec-context-annotation.pin.tstypecheckrunscheck:test-typecheck --project tsconfig.test.json,include= all ofsrcVerified non-carriers, so the next sweep does not re-open them: the
packages/core,packages/rest/src/plugin-*.pin.test.ts,packages/runtime/src/sandbox/quickjs-runner.test.ts,packages/services/service-automationandpackages/plugins/plugin-securityhits all already quote-and-correct the claim in as many words, andpackages/spec/src/contracts/objectql-engine.tsstates it in the past tense about another package.Gates
All 52 commands
scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackderives for this change set, pluspnpm lint, which this lane always adds because the deriver does not name it. Every exit code was captured before any pipe (cmd > log 2>&1; EXIT=$?).pnpm lint(repo-wideeslint . --no-inline-config, full run, zero output)PREREQUISITE NOT MET= NOT MEASUREDcheck:dual-build-cjs-loads,check:type-check-debt— both read built output for the whole workspace and this worktree built only@objectstack/rest's dependency closure. Neither a pass nor a failure; declared to CI, which builds the workspace first. Neither can be moved by a comment inside a test file.Named verdict lines rather than bare status codes, for the two gates closest to this card:
Gate union taken at
b45912f4, the final commit.One declared narrowing
pnpm --filter @objectstack/rest typecheckand the single-file vitest run were measured green under the heavy-verify lock before the edit (VERDICT command-exit 0). Re-running them after the edit queue-timed out twice on that lock (exit 99= NOT MEASURED, 18 minutes waited across two attempts; the holder is another dev's verify batch, held past 22 minutes, and this seat's queue place is parked, not lost).So the post-edit check is narrowed to the only property a comment-only diff can break — that the docblock still terminates where it did — and the narrowing is proven rather than assumed. The edited file's token stream is identical to the pre-edit blob's, and the control that shares the real failure mode fires:
HEAD~1)The firing control's anchor match is asserted before the reading is taken, so a mutation that failed to land voids the run instead of passing quietly. CI runs the unnarrowed package
typecheckand test suites on this branch.Changeset
skip-changeset. The whole diff is comment bytes inside a*.test.tsfile.@objectstack/restpublishesfiles: ["dist", "README.md", "CHANGELOG.md"], andci.yml's Build Core job carries a step whose whole job is to assert that no compiled test file reaches anydist— so no published path can carry this text.pnpm check:published-filesis among the 51 green gates. No changeset is added, because none of it would describe a behaviour change that happened.Acceptance notes
Two observations inside the file I was fenced to, both deliberately not fixed — they are a different defect class from this card, and the card's own fence says a prose finding does not authorise touching a case body:
RestServer's constructor now takes 21 parameters —tenancyServiceProviderwas appended last by [decision · p0] an ex-member API key reads AND writes another organization's rows on the single-kernel wiring underisolated— the wall compares against the caller's own unvetted claim #15256 — andrest-api-plugin.tspasses all 21. Left untouched on purpose: correcting the count without touching the case body would leave the prose describing a wider span than the test covers, which is the maintainer's call, and my replacement paragraphs deliberately introduce no counts that could contradict the existing sentence.PROVIDERStable stops at index 19 and does not covertenancyServiceProviderat index 20 — a hole in exactly the guard this file exists to be. The pin is green because the uncovered provider is lazy and so never appears in the lookup set the last case compares. Reported for filing; ⛔ not fixed here.Generated by Claude Code