Uh oh!
There was an error while loading. Please reload this page.
fix(docs-audit): derive the docs-drift advisory from what pages document, not package edges - #9229
Merged
os-project-manager merged 1 commit intoAug 17, 2026
Conversation
…ent, not package edges The advisory resolved pages by PACKAGE DEPENDENCY -- "which hand-written docs reference @objectstack/x". That is a dependency-graph proxy answering a semantic question, and #9192 measured it wrong in BOTH directions on PR #9191 (the three metadata read verbs): 3 pages listed of which 1 was relevant, while the 2 pages that actually document the changed surface were absent, because they document it through the SDK -- which does not depend on the implementing package at all. A page is now listed when it NAMES something the change touched. Three anchor kinds: the documentable declaration enclosing each changed line (top-level, or a member of a top-level container -- a local inside a function body is not surface, which is what drops the measured `singular` false positive); the wire routes a changed symbol's registrar handler serves; and the client method the route ledgers bind to those routes, which is the hop that carries the derivation across the surface boundary the package graph cannot cross. Two guards keep it precision-first, both running BEFORE the route bridge so a weak name cannot mint route anchors from every handler that mentions it: a shape guard (a single lowercase word like `label` or `object` matched 82 and 113 of 178 pages) and a corpus-share guard (`ObjectQL` is code-shaped and genuinely changed, and names 59 of 178 pages). Everything either guard removes is published, as is every changed file that yielded no anchor -- silence from this tool must never be readable as absence, which is the whole subject of the card. Measured before/after across ten real PRs; the PR comment now also states what the run could not see, at the point of use. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Y26DJEHSBhhAQ6wwfsHNza
Contributor
📓 Docs Drift CheckNothing 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. |
This was referenced Aug 17, 2026
os-project-manager
marked this pull request as ready for review
August 17, 2026 04:35
Uh oh!
There was an error while loading. Please reload this page.
os-project-manager
deleted the
claude/issue-9192-docs-drift-content-map
branch
August 17, 2026 04:43
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes#9192
The docs-drift advisory resolved pages by package dependency — "which hand-written docs reference
@objectstack/x". That is a dependency-graph proxy answering a semantic question, and the card measured it wrong in both directions on PR #9191: 3 pages listed of which 1 was relevant, while the 2 pages that actually document the changed surface were absent, because they document it through the SDK, which does not depend on the implementing package at all.Taking the first-touch grading's route: derive from what pages document.
The derivation
A page is listed when it names something the change touched. Three anchor kinds:
symbolroutesdkThe last two hops are what cross the boundary the package graph cannot:
auditMetaItem(changed) →GET /api/v1/meta/:type/:name/audit(rest-server.tsregistrar) →meta.getAudit(rest-route-ledger.ts) → the tokenapi/client-sdk.mdxactually contains. Both hops are declared data in the repo, not inference.A local variable is not documentable surface. That one rule drops the measured false positive:
const singular = request.type;inside a method body is not an anchor, sokernel/services-checklist.mdx— whose onlysingularis a service slot name — is gone. Aconstobject is a container (its keys are metadata property names, which docs do name); a function body is not.Two guards, both publishing what they removed
The first build of this was, on some PRs, noisier than the proxy it replaced — 134 rows where the old tool gave 26. Two guards fixed that, and both run before the route bridge, because a weak name left in the set does not merely add a noisy row: it mints noisy route and SDK anchors from every registrar handler that mentions it.
label,object,start,locale,sectionsall arrived as real declarations and matched 82, 113, 43, 13 and 10 of 178 pages. Confining them to code spans does not help; those words live in code spans too. Reported asweakAnchorsDropped.ObjectQLis code-shaped, genuinely changed, and named by 59 of 178 pages; it cannot tell an author which page to re-read. Reported asoverbroadAnchors, with the count that condemned it.Plus a cap on the bridge: a symbol wired into more than 3 routes is a cross-cutting helper (
sendErrorand co. pulled six unrelated route families into a REST error-typing change). Reported ascrossCuttingSymbols.SCREAMING_SNAKEconstants stay out of the bridge — a data table is consulted by handlers, it is not their implementation.What it cannot see is reported, never implied
anchorlessChangeslists changed files that yielded no anchor at all. The superseded coarse set is still computed and emitted aspackageMentionDocs, labelled, so an audit that wants the wide net can still ask for it — and so a reader can tell a narrow list from a blind one. The PR comment renders all of it in a collapsed "What this run could not see" section, and distinguishes "no page names any of the N anchors" from "nothing in this diff resolved to a documentable surface, so this run has no opinion". The card's central point was that the tool never signalled its own limits at the point of use; that is the half this addresses.Measured, across ten real PRs
Each re-derived at its own merge base with its own docs corpus (
docsrow counts):0668f02a6fix(rest): closedErrorCodeunion on the error responder75b7c240afeat(spec):master_detail+controlled_by_parent07ad42463fix(cli):os meta resyncskip-count explanation7a537ce90feat(spec): strict top-level stack keys445ae4debfix(auth): auth emails follow the deployment locale30b1c636afeat(spec): register 9 REST wire codes650cd3daafix(objectql): delete-cascade registry reads3851f87f0feat(spec,plugin-security): partial field maskingd5156b965refactor(metadata-protocol): drop deadobjectstolerances"One PR is not a rate" was the card's own caveat, so this is the answer to it. The #9191 row reads 4 against the card's "three pages" because
docsis the full set and the comment partitionsreleases/v9.mdxinto its own read-only section (#6893): 3 editable + 1 release-owned.On #9191 the change is qualitative, not just smaller — all three previously-listed pages are gone and the two the card measured as missing are back, each carrying the anchor that put it there:
The two zeroes are the honest shape of the trade, not a bug.
07ad42463derivesMetaResyncandresyncSkipExplanationLineand no hand-written page names either, so the run says so and points at the coarse set — where the old tool's 22 rows were every page mentioning@objectstack/cli. A CLI command name (os meta resync) is exactly the recall class the shape guard costs us: it is a lowercase word, so it cannot anchor. Stated rather than hidden.Cost, which the card flagged as never measured
The derivation reads the same 178-page corpus the old one did, plus the 18 route-registrar/ledger sources (~875 KB) and one
git showper changed file per side. End to end on the ten PRs above: 85-195 ms before, 114-582 ms after; best-of-3 on the widest diff, 92 ms → 429 ms. Every case stays well under a second against a job that already spends seconds checking out the repo and setting up Node. It is the right default for every PR.Verification
node scripts/docs-audit/check-affected-docs.mjs— the dispatch-gates discovers no family for a workflow verification step whose script is not namedcheck-*—docs-drift-check.ymlruns one onpackages/**and the tool cannot mention it in any bucket #9187 wrapper is kept and extended, not worked around: 115 self-test cases pass, up from 56. The new pins cover the rank rule (a method body anchors on the method, a local does not), container detection, statement heads, the shape guard, route-tail acceptance and matching,-U0hunk parsing, the registrar and ledger parsers, and an end-to-end bridge assertion over the three fixtures.functiona container → 2 cases red,draftBufferbecomes an anchor;3851f87f0the list goes 19 → 49 pages.Restored each time with
git checkoutof the branch path;git statusclean afterwards.3cb2c9eb8, all green:check:docs-audit-scope,check:node-version,check:required-contexts,check:shard-attestation,check:workflow-status-functions,node scripts/check-shard-attestation.mjs,node scripts/docs-audit/check-affected-docs.mjs,node scripts/check-nul-bytes.mjs. Derived withnode scripts/pm/dispatch-gates.mjsagainst the actual changed paths.--jsonoutput for three shapes (rows, zero-with-anchors, capped-with-limits).Scope
scripts/**and.github/workflows/**publish nothing ⇒ no changeset;skip-changesetapplied. No cross-tool shared rule and noAGENTS.md/ dispatch-brief change — the grading scoped this card to this tool's derivation.content/docs/releases/untouched; the release-owned partition (#4920 / #6893) and--allbehaviour are unchanged, andcheck-audit-scope.mjs, which consumes--all --json, still passes.Generated by Claude Code