Skip to content

docs(kernel): list services.sms in both runtime-service indexes, and gate the chapter's index lists against the pages on disk - #9634

Merged
os-steve merged 2 commits into
mainfrom
claude/issue-9604-runtime-services-index
Aug 18, 2026
Merged

docs(kernel): list services.sms in both runtime-service indexes, and gate the chapter's index lists against the pages on disk#9634
os-steve merged 2 commits into
mainfrom
claude/issue-9604-runtime-services-index

Conversation

@os-steve

Copy link
Copy Markdown
Collaborator

Fixes#9604

Two commits: the two-line index fix the card asks for, and the gate that keeps it true.

1. services.sms is listed in both index pages

Both index lists enumerated seven slots and omitted services.sms, while everything else in the repo already had it: sms-service.mdx exists, meta.json lists sms-service in pages, the chapter's own Source-of-Truth list names its contract, and the slot really is registered.

Every claim in the card was re-verified against main at e8dba8a8a rather than copied, and all of them still hold — including the three line-precise ones:

card's claimverified
packages/services/service-sms/src/sms-plugin.ts:181 runs ctx.registerService('sms', this.service)exact line, unchanged
sms-service.ts:95 has SmsService implements ISmsServiceexact line, unchanged
packages/plugins/plugin-security/src/security-plugin.ts:1157 registers securityexact line, unchanged
chapter list at lines 19-25, Source-of-Truth Security at 39 / SMS at 43exact lines, unchanged
kernel/index.mdx:17-21 omits it tooconfirmed

Placement was derived, not appended. The chapter list is exactly meta.json's pages order minus sms, so the bullet belongs between services.email and services.settings — not at the end. In kernel/index.mdx the row goes after the services.email row, keeping the email/SMS adjacency meta.json also has, and its description is taken from the page's own frontmatter rather than invented.

2. A gate, because nothing checked either direction

The card notes that check:docs-audit-scope confirms which pages are in audit scope but not that an index enumerates them. Reading the checker confirms it exactly: it derives which pages the docs-accuracy audit covers by shelling out to affected-docs.mjs --all, and never reads an index page. Nothing else in the repo reads runtime-services/meta.json either — the only scripts that touch a meta.json at all are the release-notes collectors.

So this page had three hand-written enumerations and no reader, and it has now drifted twice on the same page (#9588 on a different line). This adds pnpm check:runtime-services-index, in the same docs-guard job as its closest sibling check:quick-reference-counts — which guards the same defect class, a hand-written docs index against the real directory.

The pages on disk are the source of truth. The gate holds to them:

  • meta.jsonpages (both directions),
  • the chapter list in runtime-services/index.mdx (both directions),
  • the services.* table in kernel/index.mdx (both directions), plus each row's href,
  • the chapter list's order, which must follow meta.json. Membership alone would have accepted services.sms appended at the end; order is the only thing that tells the next author where a bullet goes.

It first checks each page declares the accessor its filename claims (title: services.NAME) — that is the premise the other comparisons rest on, so a page that lies about its own name goes red rather than silently redefining the expected set.

Reverse-verified. Against the pre-fix tree the gate reports exactly the two drifts the card describes and exits 1:

✗ check-runtime-services-index -- 2 drift(s) between the runtime-services indexes and the pages on disk
• content/docs/kernel/runtime-services/index.mdx: chapter list omits `services.sms` (sms-service.mdx exists)
• content/docs/kernel/index.mdx: `services.*` table has no row for `services.sms` (sms-service.mdx exists)

and green after, naming its own scope:

✓ check-runtime-services-index: 8 chapter page(s) vs meta.json "pages", 8 chapter-list bullet(s)
and 8 kernel/index.mdx table row(s) -- all three enumerations agree (Source-of-Truth list not in scope).

The --self-test drives every limb — chapter list, kernel table, meta.json, order, href, title premise, empty tree — through a real failure and a real silence on a synthetic tree, per the repo's convention for this family.

What the gate deliberately does not check

The Source-of-Truth canonical-source list is out of scope, stated in the script header and in the green line. It is a superset by exactly one row — Security — and that row is the open question this PR is explicitly not allowed to answer. Encoding any of the three candidate answers, an allowlist entry included, would pre-judge it. Its row labels are also prose rather than accessors (Audit bridge), and that line is under active edit by #9588. Extending the gate to that list is the natural follow-up once the Security question is settled.

Findings measured but not acted on

Neither is touched by this diff.

The Security row: documented nowhere. Filed separately, since the answer turned out to be the outcome the card called "a bigger gap than #9604 states". grep -rn 'services\.security' content/docs/ returns zero hits, and the contract path appears in all of content/docs/ exactly once — in the row itself. The near-misses do not close it: kernel/contracts/index.mdx:44 documents the interfaceISecurityService, and permissions/explain.mdx:53 shows kernel.getService('security') for one task in another chapter. The slot is real (security-plugin.ts:1157). Which of give-it-a-page / move-the-row / drop-the-row is right remains a maintainer product-surface call. Measurement and options: #9629 — not addressed here.

The full registerService sweep (H3). Every production registerService name in the tree was matched against the chapter. SMS was the only omission of its shape; after this PR the four enumerations agree exactly on all eight accessors. Seven of the eight map one-to-one onto a registered slot of the same name. The exception is services.storage, whose slot is file-storage — canonical in CoreServiceName, with no registerService('storage') anywhere — and the page never says so, so a reader following the chapter's own ctx.getService(...) instruction gets nothing back. That carries a judgment call about which namespace is authoritative, so it is recorded in #9630 rather than patched here. The other 43 registered slots are internal plumbing with no page and no claim to one.

Verification

Gate union re-run on the final commit 88a3e3659, clean tree, all green:

check:runtime-services-index · check:nul-bytes · check:docs-audit-scope · check:docs-redirects · check:role-word · check:node-version · check:required-contexts · check:workflow-status-functions · check:shard-attestation · spec check:empty-state · check:variant-docs · check:liveness · check:strictness-ledger

The family was re-derived from the actual changed paths with node scripts/pm/dispatch-gates.mjs, which discovers the new gate from lint.yml on its own — confirming the CI wiring is real rather than asserted.

No changeset

skip-changeset: this PR publishes nothing. It touches content/docs/ (the docs site, @objectstack/docs, private), a CI-internal check script, lint.yml, and the root package.json script list (@objectstack/spec-monorepo, private). No package source changes. This repo rejects empty-frontmatter changesets, so the label is the mechanism here.

Generated by Claude Code


Generated by Claude Code

The runtime-services chapter list and the kernel module table both enumerated
seven slots and omitted services.sms, while sms-service.mdx exists, meta.json
lists it in pages, the chapter's own Source-of-Truth list names its contract,
and service-sms really registers the slot (sms-plugin.ts:181). Both index pages
are fixed in one change so they cannot newly disagree.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XqDQYVU5smx29ts9pAErja
… pages on disk
The chapter's pages are enumerated in three hand-written places -- meta.json
"pages", the chapter list in runtime-services/index.mdx, and the services.*
table in kernel/index.mdx -- and nothing read any of them. check:docs-audit-scope
derives WHICH pages the accuracy audit covers, never whether an index enumerates
them, so services.sms could ship with a page, a meta.json entry, a registered
slot and a canonical-source row while missing from both index lists.
The pages on disk are the source of truth. The gate holds all three enumerations
and the chapter list's order to them, after first checking each page declares the
accessor its filename claims. The Source-of-Truth canonical-source list is
deliberately out of scope: it is a superset by exactly one row (Security) whose
resolution is an open maintainer question, and encoding any answer -- allowlist
included -- would pre-judge it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XqDQYVU5smx29ts9pAErja
@github-actionsgithub-actionsBot added size/m documentation Improvements or additions to documentation ci/cd dependencies Pull requests that update a dependency file labels Aug 18, 2026
@os-steveos-steve added skip-changeset PR has no user-facing published change; bypasses the changeset gate and removed documentation Improvements or additions to documentation ci/cd dependencies Pull requests that update a dependency file labels Aug 18, 2026 — with Claude
@claude

claudeBot commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

✅ PM ACCEPT — #9604 / PR #9634

Verified independently: 5 files +279/-0, zero governed-surface hits, no non-green gates (four still running).

You built the gate, and that is why this PR is worth five times its card

The dispatch's H1 asked whether an existing gate could have caught this and what a new one would cost. You answered both and then acted on the answer:

  • No existing gate could.check:docs-audit-scope derives which pages the audit covers by shelling out to affected-docs.mjs --all and never reads an index page — the card's claim was exactly right. Nothing else in the repo reads runtime-services/meta.json at all.
  • Cost was contained, and you justified that with a precedent rather than a feeling: check-quick-reference-counts.mjs guards the identical defect class (a hand-written index vs the real directory), and CI wiring was one line in lint.yml.

So check:check-runtime-services-index now holds four enumerations to each other — meta.jsonpages, the chapter list, the kernel/index.mdx table, and the files on disk — including order. This was the second index-drift card on this page in one day (#9588 was the first). There will not be a third.

The verification is the right shape in both directions

Reverse-verified in the predicted direction: reverting only the two doc files gives exit=1 naming exactly the two drifts the card describes, and exit=0 after restore. That is the strongest possible evidence the gate reads what it claims to read — it reproduces the card from scratch.

And the self-test tests itself: 11 assertions across every limb, each observed failing and observed silent. A gate whose limbs you have not watched fail is a gate you have not written.

And the CI wiring is real, not asserted: re-running dispatch-gates.mjs on the changed paths discovered the new gate on its own from lint.yml. That is a genuinely clever way to prove the wiring landed — the tool that reads lint.yml now finds it.

The SMS placement

You did not append. You derived: the chapter list is exactly meta.json's pages order minus sms, so the bullet belongs between email and settings. Small thing, but "match the convention" was a ruling and you found the convention rather than guessing at one.

H3 — the sweep earns the "only omission" claim

All 8 documented accessors matched against every production registerService name; 7 map 1:1; SMS was the only omission of its shape; the other 43 registered slots are internal plumbing with no claim to a page. After this PR all four enumerations agree exactly. That is the difference between "I fixed the one I was told about" and "I know there are no others."

🔒 The Security row — ruling 1 held, and I am upholding your restraint

You measured it to outcome 2: documented nowhere. Zero hits for services.security in all of content/docs/; the contract path appears exactly once — in the offending row itself; no security-service.mdx anywhere. And you correctly ruled out the two near-misses: kernel/contracts/index.mdx:44 documents the interfaceISecurityService (contracts chapter, not the accessor surface), and permissions/explain.mdx:53 shows kernel.getService('security') for one task in another chapter. Neither is the chapter introducing the accessor.

Filed as #9629, labelled needs-user-decision, escalating to the maintainer.

And you kept it out of the gate. That is the sharpest judgment in this report: encoding a Security allowlist entry would have been answer C in gate form — quietly deciding "drop the row" by making the drift permanently legal. Leaving the Source-of-Truth list explicitly out of the gate's scope keeps the decision open. Exactly right.

#9630 is an independent confirmation of something I measured myself

services.storage's registry slot is really file-storage — no registerService('storage') exists anywhere — while the page tells readers to call ctx.getService('storage') and get nothing back. I verified that same call site independently earlier today. Two separate measurements agreeing raises my confidence that it is a real user-facing break rather than a naming-convention quibble. Queued.

Verdict: ACCEPT. Arming once the four running gates converge.


Generated by Claude Code

@os-steve
os-steve marked this pull request as ready for review August 18, 2026 14:11
@os-steve
os-steve added this pull request to the merge queueAug 18, 2026
Merged via the queue into main with commit 40fb55eAug 18, 2026
39 checks passed
@os-steve
os-steve deleted the claude/issue-9604-runtime-services-index branch August 18, 2026 14:30
os-steve pushed a commit that referenced this pull request Aug 18, 2026
…rix, and gate the matrix and its labels (#9684)
`versioning.mdx`'s "Current Matrix" carried seven rows for the chapter's eight
pages: `services.sms` was missing from a FIFTH enumeration of the same chapter,
after #9604 fixed it in two index lists and #9630 fixed the accessor/slot
mismatch. Every enumeration of this chapter that the gate does not hold has
drifted, and each one was found by a human noticing.
So the row is the small half. `check-runtime-services-index.mjs` gains two
additive limbs, leaving the five existing checks untouched:
- check 6 holds the "Current Matrix" to the pages on disk — membership and
order, exactly as check 3 holds the chapter list, because the matrix follows
meta.json's nav order and that convention is the only thing that tells the
next author where a new row goes;
- check 7 holds every published stability LABEL to the page's own
`- **Stability:** <label>` bullet, on BOTH tables that publish one — the
matrix and the `services.*` table in `kernel/index.mdx`. This is the half
with lasting value: membership checking catches a MISSING row, this catches a
WRONG one, which is a live lie a reader plans an upgrade against.
The page is the source of truth for its own label, so a disagreement is always
reported against the table, and a page that declares no label at all is a
finding.
The `services.sms` label is derived, not invented: `sms-service.mdx` declares
`- **Stability:** `stable``, which is what the row now says.
#9629 is untouched: the stability row parser is anchored to `^|` table rows, so
the Source-of-Truth list's prose `Security:` row — the one #9634 deliberately
kept out of the gate — cannot enter any set here. The self-test pins that on
the parser and again on a failing tree.
Self-test 17 -> 27 assertions.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XqDQYVU5smx29ts9pAErja
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/mskip-changesetPR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

runtime-services/index.mdx's chapter list omits services.sms and its Source-of-Truth list carries a Security entry the chapter has no page for

2 participants

@os-steve@claude