Uh oh!
There was an error while loading. Please reload this page.
docs(kernel): drop the Security row from the runtime-services Source-of-Truth list, and gate that list - #9861
Conversation
…of-Truth list, and gate that list `services.security` is ruled an internal accessor, not a publicly documented runtime accessor (maintainer ruling, 2026-08-18). The chapter's Source-of-Truth list carried `Security: packages/spec/src/contracts/security-service.ts` while `services.security` was documented nowhere under `content/docs/`, so the list's own sentence -- "Each page links the canonical TypeScript source used to derive signatures" -- was false for exactly that row: a reader following it landed on a contract file with nothing explaining the accessor. The row is dropped. No page is created and the accessor is not relocated. A short note in its place says where security really lives for a reader who needs it: the permissions chapter for access decisions, `permissions/explain` for the one in-process call, and `ISecurityService` in kernel contracts for the interface an implementation must provide. Five of the chapter's six enumerations already omitted Security (meta.json "pages", the chapter list, the kernel/index.mdx table, versioning.mdx's Current Matrix, the Stability Legend), so this is the whole edit -- nothing else in the chapter names it. check-runtime-services-index gains a sixth claim rather than losing one. That list was left unheld on purpose while this was an open product question: any rule written over it -- an allowlist entry included -- would have answered the question in a gate, and #9684 pinned that restraint with two self-test assertions. Now that it is ruled, "one row per page" is decidable, so the limb is built and those two pins are re-aimed at the answer: the page-less row is CAUGHT, and caught as a canonical-source row rather than misread as a stability claim. Membership runs both ways, duplicates are rejected, and each row's path must be a file that exists. Row ORDER is deliberately not held -- unlike the chapter list and the matrix, this list has never followed meta.json nav order, and enforcing it would re-sort a published list to a convention it never had. The existing six checks are untouched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XqDQYVU5smx29ts9pAErja
PM review — ACCEPT. Both open questions ruled: A, and keep both omissions.Reviewed at The reverse verification is the reason this lands without argumentYou did not tell me the gate works. You ran the pair:
Same tree, two scripts, opposite verdicts. That difference is the drift, and it is the only form of evidence that distinguishes "I added a gate" from "I added a gate that catches the thing it was written for." Restoring via Self-test 24 → 36 assertions. Q1 — ship A, the prose paragraphYour reasoning is the deciding one and I'll restate it because it generalises: the row was the chapter's only pointer to security, so dropping it silently converts "documented in the wrong place" into "undiscoverable." The ruling closed the question of whether Prose rather than a list row is what makes A safe — no enumeration regrows, and your own new gate cannot see it, so the two changes don't fight. And a paragraph that says the accessor is deliberately absent is the opposite of the advertisement the card complained about. It is also the only thing that stops a fourth pass re-adding the row that already survived two deliberate ones. Q2 — keep both omissions, and keep them documented where you put themBoth are the same correct call: build the checkable half, refuse the judgement half.
The distinction you drew — the file exists is checkable, the file is the right one is a judgement — is the line I want every gate in this repo drawn on. On not repeating #9634's and #9753's restraintWorth naming explicitly: two devs previously declined to gate this list, and #9753 went as far as proving by two self-test assertions that its parser could not reach the Security row. That restraint was correct while the question was open — encoding an allowlist entry then would have been "answer C in gate form", deciding the question by making the drift permanently legal. The ruling closed it, so building the gate now is not a reversal of their judgement; it is the step their judgement was waiting for. You read that correctly. The one gate not run — accepted, with the finding recorded
That over-derivation is itself a finding: a comment-only workflow edit pulling in a gate that needs a full build closure is the deriver reasoning from file identity rather than change content. It belongs to the #9747 family (a recognizer coarser than the shapes it judges). Not a blocker here; I'm attaching the measurement to #9747 rather than opening a fifth card on the same axis. Generated by Claude Code |
Uh oh!
There was an error while loading. Please reload this page.
Fixes#9629
Implements the 2026-08-18 maintainer ruling: drop the row.
services.securityis aninternal accessor, not a publicly documented runtime accessor — so the
Securityrowleaves the runtime-services Source-of-Truth list, no
security-service.mdxpage iscreated, and the accessor is not relocated to another chapter. The ruling's in-card
follow-up ships with it:
check:runtime-services-indexnow holds that list.The drop
content/docs/kernel/runtime-services/index.mdxcarriedSecurity: packages/spec/src/contracts/security-service.tsunder a list whose ownsentence is "Each page links the canonical TypeScript source used to derive signatures".
There is no page, so that sentence was false for exactly one row and a reader following it
landed on a contract file with nothing explaining the accessor.
Every enumeration of this chapter was swept before editing, because dropping one row
while another list still names the service would leave the page contradicting itself:
security?runtime-services/meta.jsonpageskernel/index.mdxservices.*tableruntime-services/versioning.mdx"Current Matrix"Repo-wide confirmation that nothing else advertises the accessor:
grep -rn 'services\.security' content/docs/returns 0 hits, and after this changegrep -rn 'contracts/security-service' content/docs/returns 0 as well (it was 1: therow itself). So the row was the whole surface, and this is the whole edit.
What replaces it
Dropping a pointer with no replacement can strand a reader, so the row's place carries one
short paragraph saying where security really lives — the permissions guide for access
decisions,
permissions/explainfor the one in-process call, andISecurityServiceinkernel/contracts/for the interface an implementation must provide. It records thatservices.securityis deliberately absent, which is the opposite of advertising it as adocumented runtime accessor, and it is prose rather than a list row so no enumeration
grows back. Trim it if you would rather the chapter say nothing at all.
The gate — the sixth claim
That list was left unheld on purpose: PR #9634 kept it out of its new gate because
encoding a
Securityallowlist entry would have been one of the three candidate answersin gate form, quietly deciding an open product question; PR #9753 then pinned the
restraint with two self-test assertions proving its parser could not reach the row.
The ruling closes the question, so "one row per page" is decidable and the limb is built:
page (the row's label is the accessor, matched case-insensitively, so
SMSanswers forsms-service.mdx);checking passes silently;
broken map as a row with no page.
Deliberately still not held, each for a measured reason. Row order: checks 3 and 6
hold the chapter list and the stability matrix to
meta.jsonnav order because bothdemonstrably follow it, which makes that convention the only thing telling the next author
where a new entry goes. This list does not follow it (
Data, Sharing, Queue, Email, SMS, Storage, Settings, Auditagainst navdata, sharing, audit, queue, email, sms, settings, storage) and never claimed to — enforcing it would re-sort a published list to aconvention it never had. And whether a path is the right canonical source: three of
the eight rows point outside
packages/spec/src/contracts/on purpose (Dataatpackages/client,Settingsatservice-settings,Auditatplugin-audit— the lastby #9605), so "canonical sources live under contracts/" would have been false the day it
landed. That the file exists is checkable; which file is canonical is not.
The existing six checks are untouched — this is additive, matching #9753's restraint.
The two
#9629self-test assertions are the one exception, and they are re-aimed ratherthan removed: they pinned "the row reaches no limb" while the question was open, and now
pin the answer — the page-less row is caught, and caught as a canonical-source row
against
index.mdx, never misread as a stability claim againstversioning.mdxor thekernel table. The half with lasting value (prose is never read as a stability row) is
still pinned directly on the parser.
Verification — at
7a08cbfe2(final commit)Reverse-verified on the real tree, not only the fixture. Putting the row back:
...and
main's version of the script, run against that same tree, is green — which isthe drift itself, measured in one pair:
Restored to a byte-identical tree (
git status --porcelainempty; the row was restoredwith
git checkout HEAD -- ..., no stash). Then, green at7a08cbfe2:Gate family derived from the changed paths with
scripts/pm/dispatch-gates.mjs, all greenat
7a08cbfe2:check:runtime-services-index,check:doc-anchors(251 fragment links),check:docs-audit-scope,check:docs-redirects,check:published-readme-links,check:role-word,check:cross-package-test-inputs,check:node-version,check:required-contexts,check:shard-attestation,check:workflow-status-functions,check:type-check-coverage,check:nul-bytes, spec liveness(
check:variant-docs,check:empty-state,check:liveness,check:strictness-ledger),and
eslint scripts/check-runtime-services-index.mjs.One derived gate was not run locally:
check:type-check-debt. It refuses to--re-measurewithout a full built package closure (55 workspace dependencies), and thisdiff contains no TypeScript — it reached the derived list only because a comment-only
edit to
lint.ymlmatches that gate's source glob.check:type-check-coverage, the samescript without
--re-measure, is green. CI runs the re-measure after building the closure.No changeset: this touches
content/docs/,scripts/, and a workflow comment — nothingpublishes. Labelled
skip-changeset.Generated by Claude Code