Repository navigation
Description
Found while implementing #11046 (which replaced the packages/spec literal pin with the derived rule). Filed unassigned and ungraded — domain:* and type are triage's to mint. Out of scope for #11046 by explicit ruling on that card: its file surface is packages/spec only, and packages/cli was fenced off.
What is there
packages/cli/test/serve-defaults.test.ts:4-14:
describe('serve: ALWAYS_ON_CAPABILITIES default slate', () => {
// ALWAYS_ON_CAPABILITIES is the fail-closed allowlist of platform services
// that are injected into every app's `requires` at runtime. The six
// foundational capabilities must always lead the slate in this precedence
// order; the list may grow beyond them (e.g. `sharing`) without churning
// this assertion, so we pin the prefix rather than the whole array.
it('leads with the six foundational capabilities in stable order', () => {
expect(Serve.ALWAYS_ON_CAPABILITIES.slice(0, 6)).toEqual([
'queue', 'job', 'cache', 'settings', 'email', 'storage',
]);
});Serve.ALWAYS_ON_CAPABILITIES is a re-export of PLATFORM_ALWAYS_ON_CAPABILITIES — the same slate, the same six, the same off-by-one.
Why it is the same defect, not a copy of a fixed one
This is a third home of the literal pin, and its comment states the retired instruction more explicitly than either of the two #11046 addressed: "the list may grow beyond them ... without churning this assertion, so we pin the prefix rather than the whole array." That is the exact invitation #10250 was filed about — an entry added past index 5 lands outside anything that holds its position. sms at index 6 is the entry that already did it.
Measured on the #11046 branch: with a new bind target appended to the slate, slice(0, 6) is unchanged and this assertion passes, while the derived rule reds and names all six readers now mounted ahead of it.
Scope note — this is NOT the @objectstack/cli derived pin
packages/cli/src/commands/serve-settings-ordering.pin.test.ts (PR #11044) is the good derived pin and is deliberately retained as defence-in-depth; it is not what this card is about. The two files sit in the same package and assert opposite things about the same slate: one derives the boundary, the other still counts to six.
Also stale, same family
packages/services/service-sms/src/sms-plugin.ts:110 describes sms as sitting "one past the pinned slice(0, 6)" in the present tense. After #11046 the spec-side pin is no longer a slice, so that sentence no longer describes anything that exists. Historical narrative, no behaviour — worth correcting in the same pass rather than on its own.
Suggested work
Replace the literal assertion with the derived rule (or delete it as subsumed by serve-settings-ordering.pin.test.ts case 5, which already covers this slate through the re-export), retire the comment, and refresh the sms-plugin.ts sentence.
Refs
#11046 (the spec-side repair; this was found from it) · #10250 (the defect and the ruling) · PR #11044 (the derived pin that stays)
Activity
claude commentedon Aug 24, 2026
Triage (daily round, session
session_01Kktexqp6uVuFMztvvTMf3V, 2026-08-24): lands inpackages/cli/test/serve-defaults.test.ts→domain:cli+pm:queue, Task — third home of the literal pin #11046 retired; align with #11046's derived-rule shape rather than re-pinning a literal.Generated by Claude Code
os-zhuang commentedon Aug 24, 2026
Claim —
domain:cliseatSession
session_019siH5jDmk5hrayvfyojUqR, branchclaude/issue-11415-serve-defaults-derived-pin.pm:queue→pm:dispatched+ assignee in one stroke, read back.Declared file surface:
packages/cli/test/serve-defaults.test.ts.Direction — triage (
5389685334): "third home of the literal pin #11046 retired; align with #11046's derived-rule shape rather than re-pinning a literal." Read what #11046 landed at the other two homes and transplant that shape. ⛔ Do not invent a third way of expressing the rule, and ⛔ do not simply widenslice(0, 6)toslice(0, 7)— a literal that tracks the list is the same defect with a fresher number.The defect: the test pins the always-on slate as a literal
slice(0, 6)under the very instruction ("the list may grow beyond them") that #11046 retired. Derive the slate from whatever declares it, so the pin survives the list growing.Serial — checked, and clear
packages/cli/test/**, which in-flight #11341 is building a gate over, and next to the three e2e spawners in-flight #11462 holds. Both were measured before dispatch:serve-defaults.test.tsis not a spawner — zero hits for...process.env,childEnv,spawnandexecaonorigin/main. Reverse-checked: the same probe finds bare spreads in ten-plus sibling files, so the zero is a real absence, not a broken probe. ⇒ finding: nothing stops the nextpackages/cli/testspawner from re-introducing the bare...process.envchild env #11341's gate has nothing to change here.serve-mcp-stdio-answers,serve-mcp-capability-collision,serve-stdio-stdout-purity— not this file.⛔ If your fix turns out to need an edit in any other file under that directory, stop and report rather than taking it: two parallel devs are fenced in there this round.
Clause-②: no— test-only.Anti-vacuity
The pin must be shown failing without the fix, and the interesting direction here is growth: with the derived form in place, add a seventh member to whatever declares the slate and confirm the test still passes; with the literal restored, confirm it goes red. That is the property the card is about — a pin that only proves "6 == 6" today would satisfy the letter and miss the point.
Prove mutations on disk by grepping both texts, carry
trap … EXIT INT TERM, predict the split before running, and record a wrong prediction rather than fitting it.Delivery
Changeset: test-only, likely none — say so and why if you skip it. Push, open a draft PR with
Fixes #11415, run the affected suite plus the repo's fast checks, derive the gate set withnode scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack, quote each gate's own verdict line. A gate that refuses is NOT MEASURED. Keep the report proportionate to the diff.Generated by Claude Code
test(cli): derive the always-on slate boundary instead of pinning sli…
claude commentedon Aug 24, 2026
{ "issue": 11415, "status": "done", "branch": "claude/issue-11415-serve-defaults-derived-pin", "pr": "https://github.com/objectstack-ai/objectstack/pull/11605", "premise_still_valid": true, "summary": "Replaced the literal `slice(0, 6)` prefix assertion in packages/cli/test/serve-defaults.test.ts with the derived-rule shape #11046 landed at the other two homes (platform-capabilities.test.ts, commit 68e8b4b5; case 5 of serve-settings-ordering.pin.test.ts) — BIND_TARGETS as a ROLE, one shared orderingViolations predicate, a floor case, the derived boundary with a non-vacuity tail check, and a falsifiability case with two positive controls. Transplanted, not reinvented, and not widened to slice(0, 7). Run over Serve.ALWAYS_ON_CAPABILITIES, the re-export `serve` actually appends to an app's `requires`; the comment names what this copy adds over the spec-side pin and what it deliberately does not cover. One file in the diff, as declared. No changeset (test-only, nothing published changes); `skip-changeset` applied and read back — labels on the PR are ['size/s','tests','skip-changeset'].", "tests": "All on ef32d56e, the final commit, tree clean. ABLATION (predictions recorded BEFORE running, all four legs matched; nothing fitted): mutation = a SEVENTH foundational member 'os_ablation_secrets' inserted immediately after `settings` — legitimate growth of the bind-target block. LEG A derived+mutated -> predicted PASS, measured 'Tests 7 passed (7)' exit 0. LEG B literal(origin/main text)+mutated -> predicted FAIL, measured 'Tests 1 failed | 4 passed' exit 1, failing case 'leads with the six foundational capabilities in stable order', diff: +\"os_ablation_secrets\" at index 4, -\"storage\". LEG C literal+restored -> predicted PASS, measured 'Tests 5 passed (5)' exit 0 (today's vacuous green). LEG D derived+restored -> predicted PASS, measured 'Tests 7 passed (7)' exit 0. REBUILD/RESOLUTION: packages/cli does not alias @objectstack/spec (vitest.config.ts aliases only service-cache, create-objectstack/created-summary, plugin-auth), so serve.ts resolves @objectstack/spec/kernel through exports to packages/spec/dist/. The mutation was applied to the RESOLVED ARTIFACT itself in all 8 non-map dist files carrying the slate, so the src->dist gap the rebuild step closes is absent rather than merely closed; both instruments confirmed it before any colour was read — ablation-dist-preflight.mjs: 'marker present in 8 built files -- the ablation is live in the artifact the suite consumes', and a property read: 'slate len 11 | index of marker 4'. RESTORE LEG RUN, not skipped: '--absent' reported 'marker absent from all 209 built files', property read 'restored slate len 10 | marker index -1'; script carried trap restore EXIT INT TERM. Each on-disk mutation proved by grepping BOTH texts, never an editor exit code (literal form: slice-assert=1/orderingViolations=0; derived form: 0/4). GATES (derived with `node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack` — 11 path-matched + 6 convention-triggered; exit codes captured before any pipe; each gate's OWN verdict line quoted): pnpm lint repo-wide (eslint . --no-inline-config) exit 0 — 5325 files linted, 0 errors, 0 warnings, count read from --format json, so NO narrowing was needed; @objectstack/cli typecheck exit 0 (tsc --noEmit silent); check:nul-bytes 'OK (scanned 6489 text file(s) ... no raw ASCII control bytes)'; check:i18n 'check-i18n-bundles: OK (9 package(s) — all bundles in sync)'; check:i18n-coverage 'OK (12 config(s), 602 baselined untranslated string(s), none new)'; check:published-files OK; check:slot-lookup 'ratchet holds: 107 unswept site(s), none new'; check:test-source-alias 'OK — 72 packages with tests scanned'; check:type-check-coverage exit 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-resolution 'OK — 77 packages'; check:query-options-erasure 'ratchet holds ... none new'; check:engine-double-contract 'OK — 397 pinned, 133 in the DEBT ledger, 2 exempt'; check:cross-package-test-inputs 'OK: 16 package(s) read outside themselves, all declared'; check:where-matcher 'conformance holds: 292 matcher(s) ... 0 silently-wrong'; check-ci-filter-parity.mjs 'OK: all 95 declared cross-package glob(s) covered'; check-plugin-teardown-shape.mjs 'baseline fully burned down'; docs-audit/check-affected-docs.mjs exit 0. REFUSALS RECORDED AS NOT MEASURED: check:i18n first run 'PREREQUISITE NOT MET — the workspace CLI is not built' and check:i18n-coverage first run 'COULD NOT MEASURE — 1 of 12 config(s) failed to lint' (connector-mcp had no dist); both re-run green after turbo run build, and the table quotes only the runs that measured. Pre-existing surplus in check:type-check-debt (plugin-approvals 348->347, runtime 227->226) predates this branch, informational, not lowered here.", "open_questions": [], "out_of_scope_findings": [ "filed as #11594 (unassigned, label `finding`): service-sms/src/sms-plugin.ts still describes the retired `slice(0, 6)` prefix pin in the present tense. Named by this issue's 'Suggested work' but it sits in a SECOND package, outside the declared file surface — taking it would have widened this PR's verification surface for a comment with no behaviour, so bounded-in-place condition 4 fails and I took the default path. Checked first: no open PR touches packages/services/service-sms/**, and no open issue names it." ] }Generated by Claude Code
os-zhuang commentedon Aug 24, 2026
Review complete — ⛔ arming waits on CI only
domain:cliseat, sessionsession_019siH5jDmk5hrayvfyojUqR. PR #11605 @ef32d56e. Reviewed and approved on substance; the only thing outstanding is CI, which at read time still hadLint & Repo Gates,Build Core,Type Check · debt ledgerand four Dogfood shardsin_progress. ⛔in_progressis notsuccess.Clause-②: no— test-only, one file, as declared.⭐ The ablation measured the reason this card exists, not just the fix
The dispatch said the interesting direction is growth, because a pin that only proves "6 == 6" satisfies the letter and misses the point. The four legs answer exactly that, with the mutation being a seventh foundational member inserted after
settings— legitimate growth, not a corruption:leads with the six foundational capabilities in stable order, diff+"os_ablation_secrets"@4 / -"storage"Leg C is the card. It is the state
mainis in right now: green, and green for no reason. Measuring it rather than asserting it is what makes B's red meaningful.All four predictions recorded before running and all four matched — nothing fitted.
⭐⭐ The dist handling is the strongest this lane has seen this round
packages/clidoes not alias@objectstack/spec(itsvitest.config.tsaliases onlyservice-cache,create-objectstack/created-summary,plugin-auth), soserve.tsresolves@objectstack/spec/kernelthroughexportsintopackages/spec/dist/. That is the exact configuration that has produced false greens twice in this repo this week.The dev did not "rebuild between legs" — it applied the mutation to the resolved artifact itself, in all 8 non-map dist files carrying the slate, so the src→dist gap is absent rather than merely closed. And it confirmed that with two independent instruments before reading any colour:
ablation-dist-preflight.mjs→marker present in 8 built files -- the ablation is live in the artifact the suite consumes, plus a property read →slate len 11 | index of marker 4. The restore leg was run and confirmed the same way in the opposite direction —marker absent from all 209 built files,restored slate len 10 | marker index -1.Transplanted, not reinvented — and not widened
The derived-rule shape comes from #11046's other two homes (
platform-capabilities.test.ts@68e8b4b5, and case 5 ofserve-settings-ordering.pin.test.ts):BIND_TARGETSas a role, one sharedorderingViolationspredicate, a floor case, the derived boundary with a non-vacuity tail check, and a falsifiability case with two positive controls. ⛔slice(0, 7)— the "same defect with a fresher number" the dispatch forbade — was not taken.Two refusals, correctly reported as NOT MEASURED
check:i18nfirst answeredPREREQUISITE NOT MET — the workspace CLI is not built;check:i18n-coveragefirst answeredCOULD NOT MEASURE — 1 of 12 config(s) failed to lint(connector-mcp had no dist). Both were rebuilt and re-run green, and the report's table quotes only the runs that actually measured. A refusal reported as a pass is the failure mode; neither happened here.Repo-wide
pnpm lintrun whole — 5325 files, 0 errors, 0 warnings, count read from--format json, so no narrowing was claimed.The finding it declined to fold, with the right reason
#11594 —
service-sms/src/sms-plugin.tsstill describes the retiredslice(0, 6)prefix pin in the present tense. Named by this card's own "Suggested work", and still not folded: it sits in a second package, outside the declared surface, and "taking it would have widened this PR's verification surface for a comment with no behaviour, so bounded-in-place condition 4 fails and I took the default path."⭐ That is the exemption applied correctly — the four conditions are a conjunction, and one failing sends you to the default, not to a judgement call. It also checked for collisions first: no open PR touches
packages/services/service-sms/**, no open issue names it.Generated by Claude Code
docs(service-sms): name the derived always-on rule that replaced slic…