chore(ci): re-admit policy docs into ci-cache-hash before doc relocation

docs/**/*.md is deny-listed from the CI cache hash (test-irrelevant). The
CLAUDE.md restructure moves test/release POLICY into docs/TESTING.md +
docs/RELEASING.md, which DO carry contracts the test suite reads. Without
re-admitting them, a policy-only edit would produce the same cache hash and
skip the test shard that runs the build-llms + doc-history guards (false-pass).

Adds an ALLOW_PATTERNS re-admit step after the deny, scoped to the named
policy docs (not a blanket docs un-deny). Lands FIRST, before any doc moves.

Pinned by 3 new cases in test/scripts/ci-cache-hash.test.ts: TESTING.md +
RELEASING.md edits MUST change the hash; docs/guide.md still must not.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-06-02 08:34:05 -07:00
co-authored by Claude Opus 4.8
parent 284c50a488
commit 75992b77fb
2 changed files with 73 additions and 0 deletions
+29
View File
@@ -30,6 +30,14 @@
# - everything else under src/, test/, scripts/, .github/, package.json,
# bun.lock, tsconfig*.json, the schema files — obviously test-affecting
#
# POLICY-DOC RE-ADMIT (the docs/ exception): some docs/*.md files carry
# CI / release / test CONTRACTS that the test suite reads (e.g. the
# build-llms content-contract test, the doc-history guard). The broad
# `^docs/.*\.md$` deny above would let a policy edit to those skip CI — a
# false-pass. The ALLOW_PATTERNS list below re-admits them into the hash
# AFTER the deny. ADD a path there whenever you move a policy/contract doc
# under docs/ (current entries: docs/TESTING.md, docs/RELEASING.md).
#
# Locale-stable: LC_ALL=C on the sort step so byte-order is identical
# across runners (different default locales would re-order the line list
# and change the final hash).
@@ -113,6 +121,27 @@ DENY_RE=$(printf '\t(%s)' "$DENY_ALT")
# TODOS\.md$|docs/.*\.md$|...)`. Each alternative anchors its own end.
INCLUDED=$(printf '%s\n' "$LS_FILES" | grep -vE "$DENY_RE" || true)
# Re-admit test-affecting policy docs that live under docs/ but carry CI /
# release / test contracts. The broad `^docs/.*\.md$` deny above removed
# them; without this re-admit a policy edit to docs/TESTING.md or
# docs/RELEASING.md would produce the SAME hash and skip the test shard
# that runs the build-llms + doc-history guards — a false-pass. Patterns
# anchor on the `\t<path>` boundary in `git ls-files -s` output, matching
# the deny-list convention above. Re-admitted lines that don't exist yet
# (pre-relocation) simply match nothing.
ALLOW_PATTERNS=(
'\tdocs/TESTING\.md$'
'\tdocs/RELEASING\.md$'
)
ALLOW_ALT=""
for p in "${ALLOW_PATTERNS[@]}"; do
if [ -z "$ALLOW_ALT" ]; then ALLOW_ALT="$p"; else ALLOW_ALT="$ALLOW_ALT|$p"; fi
done
READMIT=$(printf '%s\n' "$LS_FILES" | grep -E "($ALLOW_ALT)" || true)
if [ -n "$READMIT" ]; then
INCLUDED=$(printf '%s\n%s\n' "$INCLUDED" "$READMIT" | grep -v '^$' | LC_ALL=C sort -u)
fi
if [ -z "$INCLUDED" ]; then
echo "error: every tracked file is deny-listed — refusing to hash empty set" >&2
exit 1
+44
View File
@@ -400,6 +400,50 @@ describe("ci-cache-hash.sh — edge cases", () => {
});
});
describe("ci-cache-hash.sh — policy-doc re-admit (test-affecting docs under docs/)", () => {
// These docs live under docs/ (normally deny-listed) but carry CI/release/
// test contracts the suite reads. The re-admit must make edits to them
// invalidate the hash, WITHOUT un-denying ordinary docs.
const POLICY_FILES: Record<string, string> = {
...BASELINE_FILES,
"docs/TESTING.md": "# Testing\n\ntest tiers + isolation lint\n",
"docs/RELEASING.md": "# Releasing\n\nversion locations + ship process\n",
};
function withSandbox(test: (sb: Sandbox) => void) {
const sb = makeSandbox(POLICY_FILES);
try {
test(sb);
} finally {
rmSync(sb.dir, { recursive: true, force: true });
}
}
it("docs/TESTING.md edit MUST change hash (re-admitted policy doc)", () => {
withSandbox((sb) => {
const before = hash(sb);
modify(sb, "docs/TESTING.md", "# Testing\n\nNEW POLICY\n");
expect(hash(sb)).not.toBe(before);
});
});
it("docs/RELEASING.md edit MUST change hash (re-admitted policy doc)", () => {
withSandbox((sb) => {
const before = hash(sb);
modify(sb, "docs/RELEASING.md", "# Releasing\n\nNEW SHIP RULE\n");
expect(hash(sb)).not.toBe(before);
});
});
it("docs/guide.md edit STILL produces same hash (re-admit is scoped, not a blanket docs un-deny)", () => {
withSandbox((sb) => {
const before = hash(sb);
modify(sb, "docs/guide.md", "# Guide v3\n");
expect(hash(sb)).toBe(before);
});
});
});
describe("ci-cache-hash.sh — usage errors", () => {
it("--bogus arg exits 2", () => {
const r = spawnSync("bash", [SCRIPT_SRC, "--bogus"], {