You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(ci): docs-drift advisory names the tree it was computed on (#9519) (#9524)
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.
Claude-Session: https://claude.ai/code/session_01XqDQYVU5smx29ts9pAErja
Co-authored-by: Claude <noreply@anthropic.com>
// A sha that misidentifies the tree is worse than no sha, so the one
182
+
// condition under which it does is stated right where the sha is.
183
+
if (computedOn.dirty === true) {
184
+
lines.push('', '⚠️ That checkout carried **uncommitted changes**, so the commit above does not fully identify what was read.');
185
+
} else if (computedOn.dirty === null) {
186
+
lines.push('', '⚠️ This run could not check whether its checkout was clean, so the commit above may not fully identify what was read.');
187
+
}
188
+
lines.push('</details>');
189
+
return lines;
190
+
})();
102
191
const limits = [];
103
192
if (anchorless.length) limits.push(`**${anchorless.length}** changed file(s) yielded no anchor (\`${anchorless.slice(0, 3).join('`, `')}\`${anchorless.length > 3 ? ', …' : ''}) — pages documenting those are invisible to this run`);
104
193
if (crossCutting.length) limits.push(`**${crossCutting.length}** cross-cutting symbol(s) contributed no route anchor: \`${crossCutting.join('`, `')}\``);
@@ -137,7 +226,13 @@ jobs:
137
226
const headline = anchorList.length === 0
138
227
? `Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from **${pkgs.length}** changed package(s)), so **this run has no opinion** about the docs.`
139
228
: `**${anchorList.length}** anchor(s) derived from **${pkgs.length}** changed package(s); no hand-written page names any of them. ✅`;
140
-
body = [marker, '### 📓 Docs Drift Check', headline, ...limitsBlock].join('\n');
229
+
// The tree identity is rendered here only when anchors WERE derived: that
230
+
// is the run with an opinion ("no page names any of them"), and a reader
231
+
// whose own tree does yield a row is owed the reason. With no anchors the
232
+
// run has no opinion to disagree with, and the bytes would be pure noise on
233
+
// every docs-tooling-only PR (#9037 — every byte here is relayed).
234
+
body = [marker, '### 📓 Docs Drift Check', headline, ...limitsBlock,
`**${editable.length}** hand-written doc(s) name something this change touched — list omitted above ${EDITABLE_ROW_CAP} rows. Re-derive: \`node scripts/docs-audit/affected-docs.mjs --json origin/${baseRef}\`.`,
252
+
`**${editable.length}** hand-written doc(s) name something this change touched — list omitted above ${EDITABLE_ROW_CAP} rows. Re-derive on the tree named below: \`${rederive}\`.`,
158
253
);
159
254
if (readOnly.length) {
160
255
body.push(
@@ -186,13 +281,21 @@ jobs:
186
281
}
187
282
}
188
283
body.push(...limitsBlock);
284
+
body.push(...treeBlock);
189
285
body.push(
190
286
'',
191
287
'> Advisory only, and a **precision-first** one (#9192): a page is listed because it names a',
192
288
'> symbol, wire route or SDK method this diff touched — not because it mentions a changed',
193
289
'> package. Each row says which anchor put it there, so a wrong row is reportable rather than',
194
290
'> merely annoying. To re-verify, run the `docs-accuracy-audit` workflow scoped to these files:',
195
-
'> `node scripts/docs-audit/affected-docs.mjs origin/' + baseRef + '` → pass the list as `args.docs`.',
291
+
// Pinned to the commit the mapper measured from, and pointed at the tree
292
+
// named above (#9519): run this against a different tree and a different
293
+
// list is the CORRECT answer — which is exactly how a right row once got
294
+
// reported as a wrong one.
295
+
'> `node scripts/docs-audit/affected-docs.mjs ' + (diffBase || `origin/${baseRef}`) + '` → pass the list as',
296
+
computedOn.head
297
+
? '> `args.docs`, on the commit named under **Which tree this was computed on**.'
0 commit comments