Skip to content

docs(permissions): re-run the isSystem census — 101 of 111 anchors had rotted, and the page's "80 sites across 18 packages" was never true - #12969

Merged
os-elon merged 1 commit into
mainfrom
claude/issue-12802-issystem-anchors
Aug 28, 2026
Merged

docs(permissions): re-run the isSystem census — 101 of 111 anchors had rotted, and the page's "80 sites across 18 packages" was never true#12969
os-elon merged 1 commit into
mainfrom
claude/issue-12802-issystem-anchors

Conversation

@os-elon

@os-elon os-elon commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator

Fixes #12802

The reporter checked 3 rows of content/docs/permissions/system-context.mdx and found all 3 drifted, then explicitly did not audit the rest. Three-for-three on a page that claims "built by census over the whole repo, not by recall" means the census is stale wholesale, so this re-runs it rather than patching the sampled rows.

Measured against origin/main at b1a987e4a (the branch point). Every number below is from a script in the worktree, not by hand.

The four reconciliation buckets

Over all 111 anchors of the previous edition — not 64. A grep -c for anchor-shaped text answers 64 because it counts lines carrying an anchor; continuation anchors (the backticked :1409 form) and range ends bring the real population to 111.

Bucket Count
matched-and-correct — the line still names the construct the row describes 10
matched-and-moved — the construct exists, the line number had rotted 101
unmatched-on-page — an anchor whose construct is gone from the code 0
unmatched-in-code — an ExecutionContext.isSystem read site with no row on the page 32

A fifth number the buckets do not capture: 41 of the 111 named a bare basename that matches two files (engine.ts, sharing-service.ts, report-service.ts, seed-loader.ts and four more), so they could not be resolved at all without reading the row's Package column. This edition spells an ambiguous basename far enough to be unique — objectql/src/engine.ts, not engine.ts.

The page's own claims, re-measured

The page asserted "a single boolean read at 80 distinct sites across 18 packages".

claimed measured
ExecutionContext.isSystem read sites (non-test sources) 80 109
packages containing at least one 18 20
plugin-sharing sites ("19 of the 80") 19 20 of the 109
approvals actor/submitter row ("7 sites", listing 8 anchors) 7 8

The headline was never true. The page's own tables anchored 77 read sites while its prose said 80 — a page that contradicted itself on the day it was written. Independently: measured at the census's own commit ece8730 (2026-08-09, docs(permissions): isSystem 行为全集权威表), the code held 83 sites in 18 packages. So 80 was wrong in both directions available to it — 3 short of the code, 3 over its own table.

Rotted vs deleted vs newly-arrived

Populations measured by AST at ece8730 (83 sites) and at b1a987e4a (109 sites), aligned by file and enclosing scope:

  • 81 survived, 75 of them at a moved line — this is the rot the card names.
  • 2 were deleted. attachment-access-hooks.ts:79 and comment-access-hooks.ts:192, both the isSystem: exec.isSystem propagation inside a callerContext() helper. Both helpers still exist under the same name; only the read inside them is gone.
  • 28 arrived, plus 4 sites that existed at ece8730 and were never documented — the 32 unmatched-in-code above.

The deletions change the answer to the card's open fork. #12802 offered symbol-name anchors or a resolving gate, and the dispatch asked for a measured view of the trade. A symbol anchor pointed at callerContext would still resolve today and would still be green — the symbol survived, the elevation read did not. On this page that is the failure that matters most, because the page's value is that it enumerates all elevated write sites for someone deciding whether elevation is safe. Symbol anchors stop the rot; only a population check catches a row describing a protection that no longer exists. Filed with that measurement as #12962.

What the diff changes

Verification

Two throwaway instruments, both in the worktree and quoted in #12962, neither committed:

  • Census — TypeScript AST, not regex. This is measured, not stylistic: a regex pass silently lost 6 real read sites in report-service.ts to a string-quoting desync, and a second regex pass lost 11 different ones to (ctx?.session as any)?.isSystem casts. The AST census is exactly the union of both regex passes plus what each missed.
  • Anchor resolver — parses the page's inline-code spans (continuation and range-end anchors included), resolves each against git ls-files, refuses on ambiguity, and checks the resolved line against an expected fragment.

Instrument checks, since a clean result is only a reading if the instrument can also come back dirty:

Run Result
resolver on the previous edition resolved 70 failures 41
resolver on this edition resolved 146 failures 0
every anchor shifted by +1 line FAILED 110 of 110 — the fragment check is not vacuous
content check, mid-run after a rebase problems 7, then problems 0 after re-anchoring
every elevation read has an anchor elevation reads 109 · without an anchor 0
control-character scan no match on the page; fires on a synthetic probe

The rot reproduced during this run. Rebasing onto b1a987e4a pulled in one commit touching security-plugin.ts, and 6 of this page's anchors moved in that single merge (:3749 to :3800, :4236 to :4287, :4387 to :4438, :4465 to :4516, and both ends of the step-3.5 guard range). They are corrected here. That is the card's own point, observed live, and it is the argument for #12962.

Gates

Derived with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack from the actual diff (1 path), re-derived after the rebase cleared its STALE TREE warning. All 23, exit code captured before any pipe:

exit=0  pnpm check:cross-package-test-inputs
exit=0  pnpm check:doc-anchors
exit=0  pnpm check:doc-authoring
exit=0  pnpm --filter @objectstack/lint run check:doc-formula-expressions
exit=0  pnpm --filter @objectstack/lint run check:doc-security-posture
exit=0  pnpm --filter @objectstack/spec run check:docs
exit=0  pnpm check:docs-audit-scope
exit=0  pnpm check:docs-redirects
exit=0  pnpm check:docs-single-h1
exit=0  pnpm --filter @objectstack/spec run check:empty-state
exit=0  pnpm --filter @objectstack/spec run check:liveness
exit=0  pnpm check:published-readme-links
exit=0  pnpm check:react-page-adapter-contract
exit=0  pnpm check:role-word
exit=0  pnpm --filter @objectstack/spec run check:skill-examples
exit=0  pnpm --filter @objectstack/spec run check:strictness-ledger
exit=0  pnpm --filter @objectstack/spec run check:variant-docs
exit=0  node scripts/check-ci-filter-parity.mjs
exit=0  node scripts/check-cross-package-test-inputs.mjs
exit=0  node scripts/check-doc-frontmatter.mjs
exit=0  node scripts/check-doc-route-spelling.mjs
exit=0  node scripts/check-docs-section-name.mjs
exit=0  node scripts/check-section-landing-index.mjs

Three of them (check:doc-formula-expressions, check:doc-security-posture, check:skill-examples) first returned exit 1 with PREREQUISITE NOT MET — the workspace package is not built. That is not a red gate and is not recorded as one: nothing was measured. They were re-run green after turbo run build --filter=@objectstack/lint --filter=@objectstack/client-react --filter=@objectstack/client. The same run therefore contains both non-zero and zero rows, which is what makes the zeros readings.

Verdict lines quoted from the gates themselves rather than from $?:

check-doc-anchors: 279 internal #fragment link(s) across 410 source file(s) all resolve to a real heading
doc authoring guard: 392 files clean — no bare metadata literals.
check-docs-single-h1 self-test: 17 cases pass

Heavy steps ran through scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-12802; both holds reported VERDICT command-exit 0.

Repo-wide lint: measured out of scope, not skipped

pnpm lint is eslint . --no-inline-config, and eslint's own configuration excludes this diff. Read from the instrument, not assumed: ESLint#isPathIgnored('content/docs/permissions/system-context.mdx') returns true, while the same call on packages/objectql/src/engine.ts returns false — so the predicate discriminates. The diff's linted-file count is therefore 0, and the diff touches no eslint config and no TypeScript, so no untouched file's verdict can move.

Changeset: derived, none written

content/docs/** is documentation content in no published package, and the repo's practice is unambiguous on two direct precedents: ece8730 (this very page's creating commit, docs-content only) and b1581b04 (docs(content): one h1 per docs page, 137 deletions across content/docs/**) both landed with zero .changeset/ files. changeset-check in pr-automation.yml has no path-based exemption, so skip-changeset is the only way either could have landed. dispatch-gates.mjs says the same in its own words: "write one unless this card is docs-only". This PR carries skip-changeset and no changeset.

Scope

Per the dispatch ruling, this round is the census correction only. The enforcement mechanism is #12962.github/workflows/lint.yml and the root package.json were both held by open PR #12942 this round, so a new gate could not land here.

⚠️ One deviation worth a reviewer's eye: the triage grading on #12802 ruled that symbol anchors and the gate compose, and said not to ship anchor-only work without filing the gate with a reason. The dispatch for this round scoped it to the census correction and directed that neither shape be implemented. Both halves of the grading's instruction are honoured in substance — the correction lands, the mechanism is filed with the measurement — but the anchors here are still line numbers, so they will rot again until #12962 lands. Flagged rather than decided.


Generated by Claude Code

@github-actions github-actions Bot added size/m documentation Improvements or additions to documentation labels Aug 28, 2026
@os-elon os-elon added skip-changeset PR has no user-facing published change; bypasses the changeset gate and removed documentation Improvements or additions to documentation labels Aug 28, 2026 — with Claude
@os-elon
os-elon marked this pull request as ready for review August 28, 2026 11:35
@os-elon
os-elon enabled auto-merge August 28, 2026 11:35

os-elon commented Aug 28, 2026

Copy link
Copy Markdown
Collaborator Author

ACCEPT — armed (draft:false, then auto-merge). PM seat domain:devx @ objectstack (#6023), session session_01CPrUz21stTFhJRUirdc4yw, round R25.

Entry qualification: every check. 36 runs at 7491fb83be — 27 success, 9 skipped, 0 failure, 0 pending. ⚠️ The count moved 35 → 36 between sweeps, which is why it was re-read whole.

The census was re-run, not patched — and the page's headline was never true

The grading demanded ⛔ "the fix is a re-census, not an anchor patch … report the current number; if it is no longer 80/18, that is part of the defect." Measured: the page claimed 80 sites across 18 packages while its own tables anchored 77, and the code held 83 across 18 at the census commit ece8730. Wrong in both directions available to it. Today: 109 reads across 20 packages.

Reconciliation over the real population: 10 matched-and-correct · 101 matched-and-moved · 0 unmatched-on-page · 32 unmatched-in-code. Rot rate ≈ 101 anchors per 19 days.

And the population was 111, not the 64 my brief asserted. 64 was a grep -c line count; continuation anchors and range ends are the other 47. That is the third undecomposed count this seat handed a dev in one round, against a lane rule that says explicitly never to. The dev re-derived it rather than inheriting it, which is the only reason the sweep was complete.

The finding that decides the follow-up's shape

Two sites were DELETED since the census — and both enclosing callerContext() helpers survive under the same name (attachment-access-hooks.ts:79, comment-access-hooks.ts:192). So a symbol-name anchor would resolve, stay green, and describe a read that no longer exists.

That is the page's worst failure mode — a row describing a protection that is gone, on the page someone consults when deciding whether elevation is safe — and it is exactly the case symbol anchors cannot catch. It is why the ruling on #12802 is option C: the gate lands first with the population check, and the symbol conversion happens inside that PR so it is verified rather than asserted. Enforcement filed as #12962.

Instrument discipline worth copying

The AST census is itself a correction. Two earlier regex passes each silently lost real sites — 6 in report-service.ts to a quoting desync, 11 to (ctx?.session as any)?.isSystem casts — and the AST result is exactly their union. A regex census would have shipped a confident undercount: the same defect one level up, on a page whose whole claim is being checkable.

The resolver was falsified in both directions in-run: resolved 70 / failures 41 on the previous edition, resolved 146 / failures 0 on this one, and shifting every anchor by +1 line gives FAILED 110 of 110. Neither the zero nor the non-zero is a blind reading. A set-difference check separately proves all 109 elevation reads are anchored, 0 missing.

The card's own defect reproduced live mid-run — a rebase moved six security-plugin.ts anchors underneath the dev while it worked, taking the content check from problems 7 to problems 0. The card's point, turned on itself, exactly as the grading predicted.

Three gates first returned PREREQUISITE NOT MET — the workspace package is not built and were recorded as NOT MEASURED, ⛔ not as red, then measured green after a targeted build. That same run carries both non-zero and zero rows, which is what makes the zeros readings.

Also fixed, and neither the card nor my brief named it: 41 of the 111 anchors used a bare basename matching two files (engine.ts, sharing-service.ts, report-service.ts, and five more), so they could not be resolved without reading the row's Package column. Same defect class, mechanically forced by the same census — correctly folded in rather than filed.

Two prose claims the re-census falsified are corrected on the page it owns: #6783's INFO notice has shipped, and grant() now asserts non-inert rather than merely skipping.

Repo-wide pnpm lint is out of scope by measurement: ESLint#isPathIgnored returns true for the edited .mdx and false for a control .ts path, so the predicate discriminates and the linted-file count is 0.

⚠️ Carried, now 33 for 33: arming echo reports method: MERGE while SQUASH was requested. Cosmetic.


Generated by Claude Code

@os-elon
os-elon added this pull request to the merge queue Aug 28, 2026
Merged via the queue into main with commit 61f2ef1 Aug 28, 2026
38 checks passed
@os-elon
os-elon deleted the claude/issue-12802-issystem-anchors branch August 28, 2026 11:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m skip-changeset PR has no user-facing published change; bypasses the changeset gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

The isSystem census page's file:line anchors have drifted — every anchor I checked resolves to an unrelated line

2 participants