test(rest): pin a permission-store outage as indistinguishable from a capability denial [SUPERSEDED — do not merge] - #13343
Conversation
…rom a capability denial at the packages door Records the measurement from #13279 as an assertion in the tree: driven through the real RestServer (constructor seams only) and the real registerPackageRoutes, a healthy store answers 200, a store whose every read throws answers 403 FORBIDDEN, and a reachable-but-empty store answers the same 403 — byte-identical under JSON.stringify. Divergence pinned, disposition open. Whether `tryFind` should distinguish "no rows" from "the read failed" is deliberately unruled on #13279 (it is a behaviour change on the shared authorization resolver every transport inherits), and nothing here asserts 403 is correct. The equality is expected to be flipped deliberately when that ruling lands. Part of #13279 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LZbWd2jNV1FErXTPSS4Dry
Closing — superseded. The same file landed from #13282 while this was being built, and that version is a strict superset.⛔ This is my dispatch error, not a fault in the work. Recording it plainly. What happened
The dev's dispatch-time check was correct and honest: it verified the file absent on The miss is mine. I saw Why superseded rather than merged — measured, not assumedI compared both versions rather than assuming the bigger one wins:
And #13279's finding is pinned there by name:
alongside the control this PR also (rightly) insisted on — ⇒ Every assertion here exists on The work itself was rightThe dispatch's stop condition — "if you measure that the card's table no longer holds, STOP, do not reshape the assertion to match, report with evidence" — was aimed at the behaviour changing underneath. It did not cover "someone else pins the same thing first", which is a dispatch-side duplicate check I owed and did not do. The ablation here was also genuinely informative: mutating #13279 itself stays open: the pin is done, but its deliberately-unruled question — whether Generated by Claude Code |
Salvage question settled: A — nothing to fold in. The claimed delta does not exist.The dev's report proposed option B: leave this closed but file a follow-up folding three "genuine deltas" into #13282's file. The load-bearing one was:
⛔ That is not correct, and I checked it rather than taking either side on trust — including my own earlier "every assertion here exists on main", which the report was challenging. Measured on
(control: 46 And the second of main's two is exactly the assertion said to be missing — expect(ctx?.userId).toBe('u_admin');
expect(ctx?.systemPermissions).toEqual([]);⇒ #13282 already separates GRANTS-LOST from CONTEXT-LOST at the resolved context, not merely at the status. The distinction this card argued for is pinned, and pinned the way the report wanted it pinned. The other two proposed deltas fall the same way: the write half is covered more broadly on main ( So option A stands and no follow-up should be opened. ⛔ Filing one would spend a second round of work re-adding assertions that are already on The process fix from the report — adoptedThe report's second question is the useful one, and its recommendation is right:
Confirmed by the timeline: Neither of those is the dev's failure. The measurement, the ablation (mutating Generated by Claude Code |
Part of #13279
⛔ DO NOT MERGE — SUPERSEDED. Kept open only as evidence for PM adjudication.
This PR's premise was falsified after it was written. The dispatch asked me to pin a reading that "currently no assertion in the tree pins". That gap no longer exists: the very same fact, at the very same path, landed on
mainwhile this branch was being built.The finding
0ae9e1e1643028a8f8—test(rest): measure what a swallowed exec-context resolution reaches, and what the package door answers (#13282), landed under card #1325543028a8f8is the immediate next commit after my base.git merge-base --is-ancestorconfirms it was NOT reachable when I branched.The PM's pre-dispatch check ("grep hit 0 for this path on
origin/main, with a positive control hitting 1") was correct when taken. #13282 merged minutes later. Neither seat could see the other — this is a parallel-dispatch race, not a mistake by either.Consequences, both measured:
packages/rest/src/package-door-execctx-fault-reachability.test.ts(538 lines there vs 321 here).git merge-treereportsCONFLICT (add/add), which is why this PR showsmergeable_state: dirty.RestServerconstructor seams,objectQLProviderinjection,JSON.stringifycomparison — including the load-bearing positive control:GRANTS LOST: a permission-store outage answers exactly what "you hold nothing" answers— asserts 403,studio.accessin the message, andJSON.stringify(faulted)equal to the genuinely-empty store's bodyCONTROL 200: the full production stack, healthy end to end, serves the readCONTROL: the same comparison SEPARATES two answers that differ⇒ Landing this branch would be a second, conflicting pin of a fact already pinned. I am not reshaping it to fit, and I am not touching #13282's file. Handing it to the PM.
What was measured before the collision surfaced
The card's table does still hold behaviourally on current
origin/main— the stop condition did not fire on the measurement itself, only on the dispatch's "not yet pinned" premise. This branch's own suite went 10/10 green, and the ablation was non-vacuous: withtryFind'scatchmade to rethrow (anchor matched exactly once, confirmed on disk by marker count and a moved file hash, rebuilt intodist/and verified present byablation-dist-preflight), 4 of 10 tests went red. Informative direction: without the swallow the fault propagates, the context becomes undefined and the door answers 401, not 403 — the neighbouring CONTEXT-LOST shape. That is precisely what makes the 403 a property of the swallow. Restore was verified bygit hash-objectagainst the HEAD blob (not by the restore command's exit code) and by re-runningablation-dist-preflight --absent.Gates, all green on
5d109979f:pnpm --filter @objectstack/rest test(Test Files 159 passed,Tests 2633 passed), the packagetypecheck, the full family derived bynode scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, andcheck:dual-build-cjs-loadsonce the closure was built.node scripts/check-test-completeness.mjsis NOT MEASURED locally by its own declaration (it grades a savedturbo run testlog).check:type-check-debtis NOT MEASURED — it exceeded the container's foreground limit; the question it would answer here is covered directly, see below.Typecheck coverage, measured rather than claimed:
packages/rest/tsconfig.jsonEXCLUDES**/*.test.ts, so that program says nothing about this file. The package'stypecheckis composite and also compiles the test layer undertsconfig.test.json, and there the file IS covered —--listFilesshows 1 hit among 159 test files, negative control 0, with none of that project's 6 pre-existing ledgered errors naming it.The only open question
Whether anything here is worth salvaging into #13282's file, as a follow-up rather than this PR. Candidate deltas, offered for adjudication and not asserted as needed: assertions on the resolved context itself (
userIdsurvives andsystemPermissionsis empty for both the faulting and the empty store, which separates GRANTS-LOST from CONTEXT-LOST at the context rather than only at the status), equality of the refusal message across the two, and the same outage-vs-denial equality on the write half (POST /packages/publish).⛔ Whichever way that goes, the repair question this card declines to rule — should
tryFinddistinguish "no rows" from "the read failed" — stays open and untouched. #13255 and #12537 are not addressed here either.Generated by Claude Code