Uh oh!
There was an error while loading. Please reload this page.
fix(ci): docs-drift advisory names the tree it was computed on - #9524
Merged
Conversation
On `pull_request`, actions/checkout gives the MERGE of base and head, and the
mapper reads every page off that working tree with readFileSync. The row set was
therefore a fact about a commit that exists on no branch a reader can name — and
the comment's own re-derivation instruction sent them to run the tool against
their own worktree, where a page that gained or lost an anchor token on the base
branch after their branch was cut yields a different list. A reader did that,
grepped, found nothing, and reported a correct row as a false positive; the
follow-up investigated a defect class this tool does not have and cost a round.
- affected-docs.mjs emits `computedOn` = { head, headParents, diffBase, dirty }.
`diffBase` is the three-dot merge-base §2 had already resolved, not `sinceRef`
re-read — naming the commit is what makes the command replayable from a clone
whose `origin/main` has moved; naming the branch is what made it a trap.
`dirty` is the field's own correctness guard: the tool reads the working tree,
so with uncommitted changes present the shas do not identify what was read.
- The workflow renders it beside the re-derivation commands, with a recipe that
lands the reader on the same tree — the ephemeral merge commit while the PR is
open, its two parents (which stay fetchable) afterwards.
- The re-derivation commands are pinned to `diffBase` instead of `origin/<base>`.
Strictly additive: `computedOn` is read off git at the emit boundary, after every
derivation has finished. Verified byte-for-byte against the pre-change mapper on
three refs — the `computedOn` block is the entire diff. Self-test 220 -> 233.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XqDQYVU5smx29ts9pAErjaContributor
📓 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 18, 2026
os-steve
marked this pull request as ready for review
August 18, 2026 07:51
Uh oh!
There was an error while loading. Please reload this page.
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#9519
Docs Drift Checkruns onpull_request, soactions/checkout@v7gives it the mergeof base and head, and
affected-docs.mjsreads every page off that working tree withreadFileSync. The row set was therefore a fact about a commit that exists on no branch areader can name — and the comment's own re-derivation instruction,
--json origin/BASEREF,sent them to run the tool against their worktree. A page that gained or lost an anchor
token on the base branch after their branch was cut yields a row they cannot reproduce.
Measured cost, per the card: a reader did exactly that, grepped, found nothing, reported a
correct row as a false positive, and the follow-up spent a full round investigating a defect
class this tool does not have (the anchor set is derived fresh per run — no cache, index or
snapshot anywhere). Not claimed here either: that any row was wrong. Only that the tree was
unnamed.
What changed
scripts/docs-audit/affected-docs.mjsemits one new JSON field:.github/workflows/docs-drift-check.ymlrenders it in a collapsed "Which tree this wascomputed on" section, in the same say what the run could not see voice the file already
uses, and pins the three re-derivation commands to
diffBaseinstead oforigin/BASEREF.Why a sha alone would not have closed it
Printing
git rev-parse HEADis what the card suggested, and on its own it hands the readeran identifier they cannot use:
refs/pull/N/mergeis ephemeral and GitHub drops it once thePR closes. Three things were needed instead, each measured rather than assumed:
headParents. The merge commit's two parents are the durable handle on that tree —both stay fetchable after the merge ref is gone, and re-merging them rebuilds it. Verified
on a live
refs/pull/9459/merge: parent 1 is the base, parent 2 is the PR head. Thatordering is GitHub's convention rather than git's, so the renderer checks it against
context.payload.pull_request.head.shaand leaves the pair unlabelled when the payloadcannot confirm it — a confidently wrong label would send a reader to rebuild the mirror
image of the tree.
diffBase, not the resolvedsinceRef. The row set depends on two commits, not one.The diff is three-dot, so
diffBasepublishes the merge-base the mapper had alreadyresolved — which makes the emitted command replay correctly from a clone whose
origin/mainhas moved on. Re-resolvingorigin/mainat emit time would have reproducedthe same trap one field down.
dirty. The tool reads the working tree, nothead's tree, so with uncommittedchanges present the sha does not identify what was read. A sha that misidentifies the tree
is worse than no sha, so the one condition under which it does is stated where the sha is.
The emitted recipe is therefore: fetch and check out the merge commit while the PR is open,
rebuild it from the two parents afterwards, then re-derive against
diffBase.Verification
The row set is unchanged — the PM's stated assumption, checked rather than asserted.
computedOnis read off git at the emit boundary, after every derivation has finished. Ranthe pre-change mapper and this one on three refs and diffed the full JSON:
docs,releaseOwnedDocs,anchors,detail,packageMentionDocs,changedPackages,anchorlessChanges, every count and every summary string: byte-identical.The emitted recipe actually reproduces the advisory. Checked out a real
refs/pull/9459/mergein a scratch worktree, ran the mapper as CI does (--json origin/main,13 docs / 6 anchors), then ran the command this PR now emits (
--json 097fe96e...) — from aclone whose
origin/mainsits well past that merge base. Identical:docs,releaseOwnedDocs,anchors,detail,packageMentionDocs,changedPackages,anchorlessChangesall compare equal.Rendering — simulated, never seen live.⚠️ A
pull_requestrun cannot be triggered fromhere, so the comment on this PR is the first real render. What was validated instead: the
Comment on PRscript was extracted from the YAML and executed against stubbedrequire/github/context/core— so the shipped source ran, not a retyped copy —across seven inputs. Two rendering defects were found and fixed this way: the opening
sentence said "every page above" in the branch that lists no pages, and the closing
blockquote pointed at a section the missing-identity branch does not render.
refs/pull/9459/merge, 13 rowsdiffBaseSelf-test 220 → 233 (12 shape cases on a pure shaper + 1 presence pin). Reverse-verified
from the committed state, each ablation restored to 233 green afterwards:
computedOnfrom the emitterdirtyprobe tofalseUntouched, per the dispatch ruling: the 503/5xx delivery-tolerance logic from #9373.
Proved rather than eyeballed — from the
Delivery, and ONLY deliverycomment to EOF, thisbranch and
origin/mainare byte-identical.Gates — derived from the actual changed paths with
node scripts/pm/dispatch-gates.mjs, run after the final commit3254f8147, all green:check:docs-audit-scope,check:node-version,check:required-contexts,check:shard-attestation,check:workflow-status-functions,node scripts/docs-audit/check-affected-docs.mjs, pluscheck:nul-bytes(any-edit family;the path derivation did not name it).
skip-changeset: this PR touches only.github/workflows/andscripts/, so it publishesnothing.
Generated by Claude Code
Generated by Claude Code