Skip to content

test(cli): derive the always-on slate boundary instead of pinning slice(0, 6) - #11605

Merged
os-zhuang merged 1 commit into
mainfrom
claude/issue-11415-serve-defaults-derived-pin
Aug 24, 2026
Merged

test(cli): derive the always-on slate boundary instead of pinning slice(0, 6)#11605
os-zhuang merged 1 commit into
mainfrom
claude/issue-11415-serve-defaults-derived-pin

Conversation

@claude

@claudeclaudeBot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Fixes#11415

The third home of the literal pin #11046 retired — and the one whose comment stated the retired instruction most explicitly: "the list may grow beyond them (e.g. sharing) without churning this assertion, so we pin the prefix rather than the whole array." That instruction is the defect (#10250): an entry added past index 5 lands outside everything holding its position, which is exactly how sms came to sit at index 6 held by nothing. Widening the slice to slice(0, 7) would have been the same defect with a fresher number.

What changed

packages/cli/test/serve-defaults.test.ts — the one file in the diff. The literal prefix assertion is replaced by the derived-rule shape #11046 landed at the other two homes (platform-capabilities.test.ts in @objectstack/spec, commit 68e8b4b5; case 5 of serve-settings-ordering.pin.test.ts), transplanted rather than reinvented:

  • BIND_TARGETS as a role — the always-on entries other entries bind into during their own kernel:ready phase — never a count;
  • one shared orderingViolations predicate, so the pin and its controls cannot drift apart;
  • a floor case (every bind target is on the slate — without it the rule constrains less without failing, and with every target gone it would constrain nothing and still pass);
  • the derived boundary itself, with a non-vacuity check that the tail is non-empty;
  • a falsifiability case carrying two positive controls: a hostile slate with settings mounted last, and the growth case the retired literal structurally could not see.

Run here over Serve.ALWAYS_ON_CAPABILITIES — the re-export serve actually appends to an app's requires. That is what this copy adds over the spec-side pin; the comment says so, and says what it deliberately does not try to cover (the membership of BIND_TARGETS, which serve-settings-ordering.pin.test.ts keeps honest by resolving the real plugin classes).

Ablation — the interesting direction is GROWTH, and it was predicted before it was run

Predictions were written down first (PREDICTIONS.txt, quoted verbatim in the report on the issue) and all four legs matched; nothing was fitted afterwards.

Mutation: a seventh foundational member os_ablation_secrets inserted into the always-on slate immediately after settings — legitimate growth of the bind-target block.

legtest formslatepredictedmeasured
Aderived (this PR)mutatedPASSTests 7 passed (7), exit 0
Bliteral slice(0, 6) (origin/main text)mutatedFAILTests 1 failed / 4 passed, exit 1
Cliteral slice(0, 6)restoredPASSTests 5 passed (5), exit 0 — today's vacuous green
Dderived (this PR)restoredPASSTests 7 passed (7), exit 0

Leg B's diff is the whole card in six lines:

 [
"queue", "job", "cache", "settings",
+ "os_ablation_secrets",
"email",
- "storage",
]

Resolution and how the mutation was proved on disk.packages/cli does not alias @objectstack/spec (packages/cli/vitest.config.ts aliases only service-cache, create-objectstack/created-summary and plugin-auth), so serve.ts resolves @objectstack/spec/kernel through that package's exports to packages/spec/dist/. The mutation was therefore applied to the resolved artifact itself, in all 8 non-map dist files carrying the slate — so the src-to-dist gap the rebuild step exists to close is not merely closed but absent. Both instruments confirmed it before any colour was believed:

  • node scripts/ablation-dist-preflight.mjs @objectstack/spec os_ablation_secrets"marker present in 8 built files -- the ablation is live in the artifact the suite consumes."
  • property read of the built artifact: slate len 11 | index of marker 4 | ["queue","job","cache","settings","os_ablation_secrets","email",...]

The restore leg was run too, not skipped: ... --absent reported "marker absent from all 209 built files", the property read returned restored slate len 10 | marker index -1, and the script carried trap restore EXIT INT TERM throughout. Each test-form swap was proved on disk by grepping both texts (ALWAYS_ON_CAPABILITIES.slice(0, 6) = 1 / orderingViolations = 0 for the literal form; 0 / 4 for the derived form) rather than trusting an editor's exit code.

Verification — all on ef32d56e, the final commit, working tree clean

Every gate below was derived with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack (11 path-matched + 6 convention-triggered families), not from a hand-built list. Each line quotes the gate's own verdict, and exit codes were captured before any pipe.

gateverdict
pnpm lint (repo-wide eslint . --no-inline-config)exit 0 — 5325 files linted, 0 errors, 0 warnings (count read from --format json, not estimated)
pnpm --filter @objectstack/cli exec vitest run test/serve-defaults.test.tsTest Files 1 passed (1) / Tests 7 passed (7)
pnpm --filter @objectstack/cli typecheckexit 0 (tsc --noEmit, silent)
check:nul-bytesOK (scanned 6489 text file(s) ... no raw ASCII control bytes)
check:i18ncheck-i18n-bundles: OK (9 package(s) — all bundles in sync, no undeclared authoring keys)
check:i18n-coveragecheck-i18n-coverage: OK (12 config(s), 602 baselined untranslated string(s), none new)
check:published-files69 publishable package(s) ... declare a files whitelist ...
check:slot-lookupslot-lookup ratchet holds: 107 unswept site(s) in 25 file(s), none new
check:test-source-aliasOK — 72 packages with tests scanned; 61 registered as still resolving a workspace dep through dist/
check:type-check-coverageexit 0
check:type-check-debt--re-measure: OK — 32 ledger entr(ies) re-measured in 248.0s, 1897 raw tsc error(s) total, none above its recorded number
check:type-source-resolutionOK — 77 packages with a tsconfig.json scanned
check:query-options-erasureratchet holds: 67 unswept non-test site(s) in 17 file(s), none new
check:engine-double-contractOK — 397 pinned, 133 in the DEBT ledger, 2 exempt
check:cross-package-test-inputsOK: 16 package(s) read outside themselves, all declared
check:where-matcherconformance holds: 292 matcher(s) discovered ... 0 silently-wrong
check-ci-filter-parity.mjsOK: all 95 declared cross-package glob(s) ... covered
check-plugin-teardown-shape.mjs63 Plugin implementation(s) across 4578 source(s) ... baseline fully burned down
scripts/docs-audit/check-affected-docs.mjsexit 0

check:i18n and check:i18n-coverage both refused on first invocation (PREREQUISITE NOT MET — the workspace CLI is not built, then COULD NOT MEASURE — 1 of 12 config(s) failed to lint). Those refusals are recorded as NOT MEASURED and the gates were re-run after turbo run build; the verdicts in the table are from the runs that actually measured.

check:type-check-debt reports two pre-existing surplus entries (plugin-approvals 348→347, runtime 227→226) that predate this branch and are untouched by a one-file test change — informational, not errors, and not lowered here.

Changeset

None, deliberately: the diff is a single test file, so nothing published changes and there is no user-visible behaviour to describe. skip-changeset is applied.

Not in this PR

The issue also names a stale sentence in packages/services/service-sms/src/sms-plugin.ts ("one past the pinned slice(0, 6)", present tense about a pin that no longer exists). It sits in a second package, outside this card's declared file surface, and carrying it would widen this PR's verification surface for a comment with no behaviour — so it is tracked separately at #11594, filed unassigned. That item is out of scope here and #11594 remains open.


Generated by Claude Code

…ce(0, 6)
The third home of the literal pin #11046 retired, and the one whose comment
stated the retired instruction most explicitly: "the list may grow beyond them
(e.g. `sharing`) without churning this assertion, so we pin the prefix rather
than the whole array." That instruction IS the defect (#10250) — an entry added
past index 5 lands outside everything holding its position, which is how `sms`
came to sit at index 6 held by nothing. Widening it to `slice(0, 7)` would have
been the same defect with a fresher number.
Transplants the derived-rule shape #11046 landed at the other two homes
(`platform-capabilities.test.ts`, and case 5 of
`serve-settings-ordering.pin.test.ts`): BIND_TARGETS as a ROLE, one shared
`orderingViolations` predicate, a floor case, and a falsifiability control. Run
here over `Serve.ALWAYS_ON_CAPABILITIES` — the re-export `serve` actually
appends to an app's `requires`.
Test-only; no changeset (nothing published changes).
Fixes#11415
The stale `slice(0, 6)` sentence in `service-sms/src/sms-plugin.ts` that the
issue also names is outside this card's declared file surface and is tracked
separately at #11594 — not addressed here.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019siH5jDmk5hrayvfyojUqR
@claudeclaudeBot added the skip-changeset PR has no user-facing published change; bypasses the changeset gate label Aug 24, 2026
@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.

@os-zhuang
os-zhuang marked this pull request as ready for review August 24, 2026 08:06
@os-zhuang
os-zhuang added this pull request to the merge queueAug 24, 2026
Merged via the queue into main with commit 76f3b44Aug 24, 2026
35 checks passed
@os-zhuang
os-zhuang deleted the claude/issue-11415-serve-defaults-derived-pin branch August 24, 2026 08:18
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/sskip-changesetPR has no user-facing published change; bypasses the changeset gatetests

Projects

None yet

2 participants

@os-zhuang@claude