Uh oh!
There was an error while loading. Please reload this page.
test(spec): derive the always-on mount boundary instead of pinning slice(0, 6) - #11416
Conversation
…ice(0, 6) The literal prefix assertion bundled the four bind targets (queue/job/cache/ settings) together with two of their readers (email/storage) and stopped one short of the third — sms, at index 6 — whose position was then held by nothing. The comment above it told the next author to grow the slate AFTER those six, which is exactly how sms came to be added outside the pin. State the rule instead: every always-on entry that is not one of the services others bind into at kernel:ready is mounted after all of them. Retire the inviting comment in the test AND its twin above the declaration itself. Part of #11046 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01T9cDbY2NBiVJWYx3BpWfH2
📓 Docs Drift CheckThis PR changes 1 package(s): ⛔ 1 release-owned page(s) name something this change touched. These are read-only:
What this run could not seeCoarse fallback — 126 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin fc21071ce0c2a1f6ffab49884f727e223ed4961a && git checkout fc21071ce0c2a1f6ffab49884f727e223ed4961a
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin bb006446adb558e5a86c009df9c0b08e98833011 61be8f8dfd1fe5d9a9e138be4de0ab71fd25cebd && git checkout -B drift-repro bb006446adb558e5a86c009df9c0b08e98833011 && git merge --no-ff 61be8f8dfd1fe5d9a9e138be4de0ab71fd25cebd
node scripts/docs-audit/affected-docs.mjs --json bb006446adb558e5a86c009df9c0b08e98833011
|
Uh oh!
There was an error while loading. Please reload this page.
Fixes#11046
Replaces the literal
slice(0, 6)always-on prefix assertion with the boundary derived from what the pin is for, and retires the comment that told the next author to grow the slate after the sixth entry.The rule, stated once
BIND_TARGETS = ['queue', 'job', 'cache', 'settings']— a role, never a count. The retired six bundled those four bind targets together with two of their readers (email,storage) and stopped one short of the third,smsat index 6, whose position relative tosettingswas then held by nothing. Bumping the 6 to a 7 would have been the same defect moved one position, so the count is gone entirely.The rule lives in one predicate (
orderingViolations) shared by the pin and its positive control, so the two cannot drift apart.Reverse verification — is the derived form actually stronger?
Measured, not asserted. The separating mutation models a new bind target arriving:
realtimejoins both the slate andBIND_TARGETS. That is the class the literal pin structurally could not see, since it constrained indices 0-5 and nothing else.slice(0, 6)PASSES— blind to itMarker counts were
0before the mutation and1/1after, so the mutation provably landed rather than being a no-op; the script restores from atrapso a mid-run kill cannot leave the tree mutated. No rebuild limb is needed here and none is claimed: the test imports./platform-capabilitiesrelatively, so vitest transforms the source directly and nodist/sits between the mutation and the assertion. (Contrastserve-settings-ordering.pin.test.ts, which reads built plugin classes and does need one.)Where this is deliberately not a superset — please read
The derived form is strictly stronger on the invariant the pin exists for, and it is unbounded in slate length. It also stops asserting two things the literal form asserted only incidentally:
queuebeforejobbeforecache), andemailandstorage.Nothing binds
queueintojobatkernel:ready, so neither was ever a mount-order constraint — pinning them was the same literalism this card retires. Bind-target membership is still asserted, by a new floor case, because the ordering rule would otherwise constrain less as targets went missing and constrain nothing at all if every one of them did.Reader membership (
email/storage/smsbeing on the slate) is no longer asserted here, and is not lost:serve-settings-ordering.pin.test.tscase 7 asserts exactly that, from the real plugin classes.The "two homes" question the card asks the implementer to answer
Keep the
@objectstack/cliderived pin. Not as tolerated duplication — the two are complementary and neither subsumes the other:packages/specsits below every plugin package, so it cannot import the plugin classes and therefore cannot verify thatBIND_TARGETSstill names the right services.That divergence is real and is now written into this file's docblock rather than left to be rediscovered: a future service that others start binding into at
kernel:readyhas to be added toBIND_TARGETSby hand, and nothing inpackages/specwill notice if it is not. The CLI pin is where that gap is closed.Scope — one declared expansion beyond the card's stated file surface
The card's premise named the inviting comment at
platform-capabilities.test.ts:137. There is a twin in the source file, atplatform-capabilities.ts:204, and it is the load-bearing one — it sits directly above the array an author edits:Retiring the instruction in the test while leaving it verbatim above the declaration would have left the card's actual target — "the comment that invites the next author to grow the slate after the sixth entry" — in place at the one spot most likely to be read. It is retired here and the rule stated in its place.
The change is comment-only. Proved mechanically rather than by eyeballing the diff — the slate's token sequence is byte-identical before and after:
Flagging it rather than assuming the expansion was wanted: this is one line of prose outside the declared surface, trivially revertible, and I would rather it be reviewed than quietly shipped.
packages/cliwas not touched, per the ruling on the card.Out of scope, filed not fixed
#11415 —
packages/cli/test/serve-defaults.test.tscarries a third literalslice(0, 6)pin onServe.ALWAYS_ON_CAPABILITIES(a re-export of this same slate), under a comment that states the retired instruction more explicitly than either comment this PR touched. Fenced off by this card's ruling, so it is recorded there and left alone here. That issue stays open; nothing in this PR addresses it.Verification
Union re-run on the final commit
61be8f8dfd.@objectstack/specfull suite419 passed (419)files,11138 passed (11138)tests@objectstack/spec typecheckcommand-exit 0—tsc --noEmit,check:scripts-typecheck,check:test-typecheck(OK — test layer compiles)check:nul-bytesEXIT=0check:engine-double-contractEXIT=0—368 (file, verb) row(s) held by the RETAINED ledgercheck:where-matcherEXIT=0—0 silently-wrong and 0 unjudged matcher(s); none newcheck:cross-package-test-inputsEXIT=0—OK: 14 package(s) read outside themselves, all declaredcheck:spec-parsed-alias·check:test-source-alias·check:query-options-erasureEXIT=0check:empty-state·check:liveness·check:strictness-ledger·check:variant-docsEXIT=0Gate families derived from the actual diff with
node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, not from a recalled list. Exit codes were captured before any pipe.Narrowed and declared:
check:type-check-debt --re-measureandcheck:type-check-coverageare left to CI. Both are moved by a new test file; this diff adds none — it edits a file already insidepackages/spec/tsconfig.test.json, whose test layer compiles clean above. Re-measuring needs the whole workspace closure built, which CI does anyway.skip-changeset: this diff publishes nothing — one test file and one comment, with the slate's tokens proved unchanged.Clause-② path limb fires on
packages/spec/src/**, so this will be held for contract review. Expected: the diff changes no contract acceptance behaviour.Generated by Claude Code