Skip to content

Commit 7f8b360

Browse files
os-zhuangclaude
andauthored
fix(pm-tooling): refuse windowed history questions a shallow clone cannot see all of (#10506)
* fix(pm-tooling): refuse windowed history questions the clone cannot see all of Three seat-run tools asked `--since` questions over a branch with no shallow guard. A shallow clone answers those from whatever part of the window is present, exits 0 and prints no warning, so each returned a confidently wrong smaller number. Agent containers clone shallow and all three run from a seat; every workflow that reads history already checks out `fetch-depth: 0`, which is why the exposure survived. - git-history.mjs: add `historyHorizon()`, the read-only half of the #9878 helper — one predicate for all adopters, never fetching, and printing a deepen command computed so it can only ADD history. - check-governed-merges.mjs: a repo whose history stops inside the window is UNAUDITED, like an absent checkout. It rendered `clean window` over ~40 governed merges. - check-engine-split-ratio.mjs: refuse rather than print an ADR trigger metric over a short denominator; annotate the horizon when the window is whole. - collect-release-notes.sh: withhold the cloud section rather than print it short, and exit 2. - lint.yml: run the three self-tests, including git-history's, which nothing in this repo ran until now. Part of #9902 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wt * fix(pm-tooling): stop the horizon annotation claiming what it has not checked Two defects found by ablating the guards rather than by reading them. - check-engine-split-ratio: the horizon line spelled "(predates the window)" unconditionally, so removing the refusal above it produced a false statement beside a wrong number instead of no statement. - collect-release-notes --self-test: the wiring pin grepped the script for a literal that also occurs in the assertion itself, so it matched its own source line and printed a tick with section 4 unwired. The needle is now assembled from two adjacent literals, and the ablation reddens it. Part of #9902 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DdCnBGcHeufjrq7drTD3wt --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
1 parent 7c7c0a7 commit 7f8b360

5 files changed

Lines changed: 656 additions & 61 deletions

File tree

.github/workflows/lint.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,50 @@ jobs:
386386
- name: Release-rehearsal clone preflight self-test
387387
run: node scripts/pm/release-rehearsal-clone.mjs --self-test
388388

389+
# Shallow-history guard self-tests (#9902). Three seat-run tools ask
390+
# WINDOWED history questions (`--since` over a branch), and a shallow
391+
# clone answers those from whatever part of the window is present, exits
392+
# 0, and prints no warning. Agent containers clone shallow, and all three
393+
# run from a seat rather than from CI — every workflow that reads history
394+
# already checks out with `fetch-depth: 0`, which is precisely why the
395+
# exposure survived: CI is not where these run.
396+
#
397+
# Measured in one container on 2026-08-21 (graft floor 2026-06-02):
398+
# `check-governed-merges` rendered `✅ clean window` for a window in which
399+
# GitHub lists 38 commits touching `docs/adr/**` and 13 touching
400+
# `AGENTS.md`; `check-engine-split-ratio --days 90` — the workflow's own
401+
# invocation — printed a 98.1% ADR trigger metric over a denominator 17
402+
# commits short, one of whose members was the graft boundary commit
403+
# itself (it carries the whole tree, so a truncated window does not just
404+
# lose data points, it fabricates one).
405+
#
406+
# What runs here is the self-tests, not the live tools: the live tools
407+
# judge whatever clone they are pointed at, and a CI checkout's depth is
408+
# the workflow's business, not a verdict this job can hold. Each pins BOTH
409+
# directions on real temp-git fixtures — a window crossing the floor is
410+
# refused, and a still-shallow clone whose floor PREDATES the window is
411+
# answered untouched, because the predicate is the floor rather than
412+
# `--is-shallow-repository` and a guard that refused provably-correct
413+
# answers would only train bypass.
414+
#
415+
# `git-history.mjs` is listed first and deliberately: it is the shared
416+
# predicate all three call, it shipped for #9878 with a self-test that
417+
# until now NOTHING in this repo ran, and an unrun self-test is a phantom
418+
# check — the AGENTS.md rule about `@ts-expect-error` in an uncompiled
419+
# file, wearing a different hat. Invoked as `node`/`bash` rather than
420+
# through `pnpm check:*`: that alias belongs in root package.json, which
421+
# is declared territory of the @changesets/cli v3 migration lane (#9465)
422+
# while it runs. Temp-dir fixtures, no network, ~6 s in total.
423+
#
424+
# (`check-governed-merges.mjs`'s own cases run in the
425+
# `pnpm check:pm-governed-merges` step above, which is already its
426+
# self-test.)
427+
- name: Shallow-history guard self-tests
428+
run: |
429+
node scripts/pm/git-history.mjs --self-test
430+
node scripts/check-engine-split-ratio.mjs --self-test
431+
bash scripts/collect-release-notes.sh --self-test
432+
389433
# Verify-lock entry-point self-test (#9661). `scripts/pm/os-verify-lock.sh`
390434
# is the ONE way an agent takes the container's shared heavy-verify lock,
391435
# and it is the enforcement of a rule that used to live only in prose: the

0 commit comments

Comments
 (0)