From 4778c0bc4d2f27420d15527c4140c269295c3e00 Mon Sep 17 00:00:00 2001 From: rene-founder Date: Tue, 7 Jul 2026 16:34:57 -0600 Subject: [PATCH 1/8] fix(ci): make whitespace guard range-aware --- .github/workflows/ci.yml | 29 ++++++++++-- tests/g19-v2-structural-check.sh | 76 ++++++++++++++++++++++++++++++++ tests/run-gates.sh | 62 ++++++++++++++++++++++++++ 3 files changed, 164 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ee03b33..e8910a3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -26,6 +26,8 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 + with: + fetch-depth: 0 - name: Setup Python uses: actions/setup-python@v5 @@ -63,8 +65,29 @@ jobs: python -m py_compile memoriaia/verify/verify-hashchain.py - name: Whitespace guard + env: + VT_WS_BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before || '' }} + VT_WS_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} run: | - git diff --check + if ! printf '%s\n' "$VT_WS_BASE_SHA" | grep -qE '^[0-9a-f]{40}$'; then + echo "WHITESPACE FAIL: base SHA is invalid ($VT_WS_BASE_SHA)" + exit 1 + fi + if ! printf '%s\n' "$VT_WS_HEAD_SHA" | grep -qE '^[0-9a-f]{40}$'; then + echo "WHITESPACE FAIL: head SHA is invalid ($VT_WS_HEAD_SHA)" + exit 1 + fi + if ! git cat-file -e "$VT_WS_BASE_SHA^{commit}" 2>/dev/null; then + echo "WHITESPACE FAIL: base commit is unavailable ($VT_WS_BASE_SHA)" + exit 1 + fi + if ! git cat-file -e "$VT_WS_HEAD_SHA^{commit}" 2>/dev/null; then + echo "WHITESPACE FAIL: head commit is unavailable ($VT_WS_HEAD_SHA)" + exit 1 + fi + WHITESPACE_RANGE="$VT_WS_BASE_SHA..$VT_WS_HEAD_SHA" + echo "WHITESPACE RANGE: $WHITESPACE_RANGE" + git diff --check "$WHITESPACE_RANGE" - name: Run verification gate suite id: run_gates @@ -180,8 +203,8 @@ jobs: - name: G-19 CI anti-theater (run-gates execution proof required) if: always() env: - VT_G19_EXPECTED_RUN_GATES_SHA: "d1ec3239d04d0cd3e716a2a6054befb76723146396f2de25550b194c36ff234b" - VT_G19_EXPECTED_STRUCTURAL_CHECK_SHA: "4863e581cfa202621ca8a1529c0fca44faa348b4df30e1eb31e84addc1432716" + VT_G19_EXPECTED_RUN_GATES_SHA: "0698b217eff2ac9933df31647b86bd77c8ce3d6f5457a1d98c1aea13bd9ca679" + VT_G19_EXPECTED_STRUCTURAL_CHECK_SHA: "19f3cb6147db94993a3f3f5e53e6dc91e5694c62d719d7b935fbde44f0b5a800" VT_G19_EXPECTED_WORKSPACE_HELPER_SHA: "e105de0bca00c509bfff6923515cccb9ab416013a20f17ff90a7159c52890a06" VT_G19_EXPECTED_VERIFY_PY_SHA: "abedd126eac0227dddad9267d8863d1b3b4921ef0bdc69301f59b9c9d708683b" VT_G19_EXPECTED_VERIFY_SH_SHA: "65694b5e0abe95f3322c45ab7c69b597d83121ec14001bdbba8e6e83f2224192" diff --git a/tests/g19-v2-structural-check.sh b/tests/g19-v2-structural-check.sh index 1c489a7..15a871b 100644 --- a/tests/g19-v2-structural-check.sh +++ b/tests/g19-v2-structural-check.sh @@ -248,6 +248,7 @@ else: GATE_STEP_NAME = "Run verification gate suite" SENTINEL_STEP_NAME = "G-19 CI anti-theater (run-gates execution proof required)" +WHITESPACE_STEP_NAME = "Whitespace guard" def find_direct_child(start, end, parent_indent, key_name): @@ -726,6 +727,19 @@ def step_env_values(step): return values +def step_with_values(step): + values = {} + for with_index in step["key_indexes"].get("with", []): + with_end = block_end(with_index, 8, SCALAR_LINES) + for index in range(with_index + 1, with_end): + if SCALAR_LINES[index]: + continue + parsed = parse_key(lines[index]) + if parsed and parsed["indent"] == 10: + values[parsed["key"]] = strip_quotes(parsed["value"]) + return values + + def writes_execution_proof_output(run_lines): code_lines = [normalize_shell_words(line.split("#", 1)[0]) for line in executable_lines(run_lines)] joined = "\n".join(code_lines) @@ -999,10 +1013,72 @@ if top_jobs_index is not None: gate_steps = [step for step in steps if step["name"] == GATE_STEP_NAME] sentinel_steps = [step for step in steps if step["name"] == SENTINEL_STEP_NAME] + whitespace_steps = [step for step in steps if step["name"] == WHITESPACE_STEP_NAME] if len(gate_steps) != 1: fail(f"expected exactly one '{GATE_STEP_NAME}' step, found {len(gate_steps)}") if len(sentinel_steps) != 1: fail(f"expected exactly one '{SENTINEL_STEP_NAME}' step, found {len(sentinel_steps)}") + if strict_workflow_proof and len(whitespace_steps) != 1: + fail(f"expected exactly one '{WHITESPACE_STEP_NAME}' step, found {len(whitespace_steps)}") + + if strict_workflow_proof: + checkout_steps = [step for step in steps if step["name"] == "Checkout"] + if len(checkout_steps) != 1: + fail(f"expected exactly one 'Checkout' step, found {len(checkout_steps)}") + else: + checkout = checkout_steps[0] + if strip_quotes(checkout["keys"].get("uses", "")) != "actions/checkout@v4": + fail("Checkout step must use actions/checkout@v4") + checkout_with = step_with_values(checkout) + if checkout_with.get("fetch-depth") != "0": + fail("Checkout step must set with.fetch-depth to 0 for range-aware whitespace verification") + + if len(whitespace_steps) == 1: + whitespace = whitespace_steps[0] + whitespace_exec = executable_lines(whitespace["run_lines"]) + whitespace_env = step_env_values(whitespace) + if "if" in whitespace["keys"]: + fail("whitespace guard must not define an if guard") + if "continue-on-error" in whitespace["keys"]: + fail("whitespace guard must not define continue-on-error") + if "shell" in whitespace["keys"] and strip_quotes(whitespace["keys"]["shell"]) != "bash": + fail("whitespace guard shell must be exactly bash when present") + if "working-directory" in whitespace["keys"]: + fail("whitespace guard must not define working-directory") + if whitespace["run_style"] == ">": + fail("folded scalar run: > found on whitespace guard") + if any(contains_neutralizer(line) for line in whitespace_exec): + fail("whitespace guard contains gate-neutralizing pattern(s)") + if len(whitespace_exec) == 1 and whitespace_exec[0] == "git diff --check": + fail("whitespace guard must not rely on bare git diff --check") + if strict_workflow_proof: + expected_whitespace_exec = [ + 'if ! printf \'%s\\n\' "$VT_WS_BASE_SHA" | grep -qE \'^[0-9a-f]{40}$\'; then', + 'echo "WHITESPACE FAIL: base SHA is invalid ($VT_WS_BASE_SHA)"', + "exit 1", + "fi", + 'if ! printf \'%s\\n\' "$VT_WS_HEAD_SHA" | grep -qE \'^[0-9a-f]{40}$\'; then', + 'echo "WHITESPACE FAIL: head SHA is invalid ($VT_WS_HEAD_SHA)"', + "exit 1", + "fi", + 'if ! git cat-file -e "$VT_WS_BASE_SHA^{commit}" 2>/dev/null; then', + 'echo "WHITESPACE FAIL: base commit is unavailable ($VT_WS_BASE_SHA)"', + "exit 1", + "fi", + 'if ! git cat-file -e "$VT_WS_HEAD_SHA^{commit}" 2>/dev/null; then', + 'echo "WHITESPACE FAIL: head commit is unavailable ($VT_WS_HEAD_SHA)"', + "exit 1", + "fi", + 'WHITESPACE_RANGE="$VT_WS_BASE_SHA..$VT_WS_HEAD_SHA"', + 'echo "WHITESPACE RANGE: $WHITESPACE_RANGE"', + 'git diff --check "$WHITESPACE_RANGE"', + ] + if whitespace_exec != expected_whitespace_exec: + fail("whitespace guard must match the exact range-aware command sequence") + if whitespace_env.get("VT_WS_BASE_SHA") != "${{ github.event.pull_request.base.sha || github.event.before || '' }}": + fail("whitespace guard env must define VT_WS_BASE_SHA from GitHub event context") + if whitespace_env.get("VT_WS_HEAD_SHA") != "${{ github.event.pull_request.head.sha || github.sha }}": + fail("whitespace guard env must define VT_WS_HEAD_SHA from GitHub event context") if len(gate_steps) == 1: gate = gate_steps[0] diff --git a/tests/run-gates.sh b/tests/run-gates.sh index 96675c9..80724f5 100644 --- a/tests/run-gates.sh +++ b/tests/run-gates.sh @@ -858,6 +858,68 @@ if [ "$G19_FIXTURE_MISSING" -eq 0 ]; then done fi +WHITESPACE_REPO="$WORK/whitespace-range-repo" +mkdir -p "$WHITESPACE_REPO" +WHITESPACE_OUT="$ROOT/$OUT" +if ( + cd "$WHITESPACE_REPO" && + git init -q && + git config user.name "verification-tools-gates" && + git config user.email "verification-tools@example.invalid" && + git config core.autocrlf false && + printf 'alpha\n' > clean.txt && + git add clean.txt && + git commit -q -m "base" && + WS_BASE_SHA="$(git rev-parse HEAD)" && + printf 'alpha \n' > clean.txt && + git add clean.txt && + git commit -q -m "head-with-whitespace" && + WS_HEAD_SHA="$(git rev-parse HEAD)" && + git diff --check >"$WHITESPACE_OUT" 2>&1 +); then + pass "G-20 bare git diff --check misses committed whitespace on clean worktree" +else + fail "G-20 bare git diff --check misses committed whitespace on clean worktree" "$(tr '\n' ';' <"$WHITESPACE_OUT")" +fi + +if ( + cd "$WHITESPACE_REPO" && + WS_BASE_SHA="$(git rev-parse HEAD~1)" && + WS_HEAD_SHA="$(git rev-parse HEAD)" && + git diff --check "$WS_BASE_SHA..$WS_HEAD_SHA" >"$WHITESPACE_OUT" 2>&1 +); then + fail "G-21 range-aware whitespace guard rejects committed whitespace" "range-aware diff unexpectedly passed" +else + pass "G-21 range-aware whitespace guard rejects committed whitespace" +fi + +if ( + cd "$WHITESPACE_REPO" && + WS_BASE_SHA="0000000000000000000000000000000000000000" && + WS_HEAD_SHA="$(git rev-parse HEAD)" && + git cat-file -e "$WS_BASE_SHA^{commit}" 2>/dev/null && + git diff --check "$WS_BASE_SHA..$WS_HEAD_SHA" >"$WHITESPACE_OUT" 2>&1 +); then + fail "G-22 whitespace guard rejects invalid base SHA" "invalid base SHA unexpectedly passed" +else + pass "G-22 whitespace guard rejects invalid base SHA" +fi + +if ( + cd "$WHITESPACE_REPO" && + git checkout -q HEAD~1 && + WS_BASE_SHA="$(git rev-parse HEAD)" && + printf 'beta\n' > clean.txt && + git add clean.txt && + git commit -q -m "head-clean" && + WS_HEAD_SHA="$(git rev-parse HEAD)" && + git diff --check "$WS_BASE_SHA..$WS_HEAD_SHA" >"$WHITESPACE_OUT" 2>&1 +); then + pass "G-23 range-aware whitespace guard passes clean committed range" +else + fail "G-23 range-aware whitespace guard passes clean committed range" "$(tr '\n' ';' <"$WHITESPACE_OUT")" +fi + echo if [ "$FAILED" -eq 0 ]; then HEAD_SHA="$("$GIT_BIN" rev-parse HEAD 2>/dev/null || printf 'unknown')" From b58cc888a50081e3f0247094d81427a25fc32f2d Mon Sep 17 00:00:00 2001 From: rene-founder Date: Tue, 7 Jul 2026 17:45:11 -0600 Subject: [PATCH 2/8] fix(ci): harden whitespace guard semantics --- .github/workflows/ci.yml | 13 +++++- tests/g19-v2-structural-check.sh | 12 +++++- tests/run-gates.sh | 68 +++++++++++++++++++++++++++++++- 3 files changed, 88 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e8910a3..4a95889 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -68,6 +68,7 @@ jobs: env: VT_WS_BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before || '' }} VT_WS_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} + VT_WS_EVENT_NAME: ${{ github.event_name }} run: | if ! printf '%s\n' "$VT_WS_BASE_SHA" | grep -qE '^[0-9a-f]{40}$'; then echo "WHITESPACE FAIL: base SHA is invalid ($VT_WS_BASE_SHA)" @@ -85,9 +86,17 @@ jobs: echo "WHITESPACE FAIL: head commit is unavailable ($VT_WS_HEAD_SHA)" exit 1 fi - WHITESPACE_RANGE="$VT_WS_BASE_SHA..$VT_WS_HEAD_SHA" + if [ "$VT_WS_EVENT_NAME" = "pull_request" ] || [ "$VT_WS_EVENT_NAME" = "pull_request_target" ]; then + WHITESPACE_RANGE="$VT_WS_BASE_SHA...$VT_WS_HEAD_SHA" + else + WHITESPACE_RANGE="$VT_WS_BASE_SHA..$VT_WS_HEAD_SHA" + fi echo "WHITESPACE RANGE: $WHITESPACE_RANGE" - git diff --check "$WHITESPACE_RANGE" + if git diff --name-only "$WHITESPACE_RANGE" | grep -qE '(^|/)\.gitattributes$'; then + echo "WHITESPACE FAIL: .gitattributes changed in checked range ($WHITESPACE_RANGE)" + exit 1 + fi + git -c core.attributesFile=/dev/null diff --check "$WHITESPACE_RANGE" - name: Run verification gate suite id: run_gates diff --git a/tests/g19-v2-structural-check.sh b/tests/g19-v2-structural-check.sh index 15a871b..c57d46d 100644 --- a/tests/g19-v2-structural-check.sh +++ b/tests/g19-v2-structural-check.sh @@ -1069,9 +1069,17 @@ if top_jobs_index is not None: 'echo "WHITESPACE FAIL: head commit is unavailable ($VT_WS_HEAD_SHA)"', "exit 1", "fi", + 'if [ "$VT_WS_EVENT_NAME" = "pull_request" ] || [ "$VT_WS_EVENT_NAME" = "pull_request_target" ]; then', + 'WHITESPACE_RANGE="$VT_WS_BASE_SHA...$VT_WS_HEAD_SHA"', + "else", 'WHITESPACE_RANGE="$VT_WS_BASE_SHA..$VT_WS_HEAD_SHA"', + "fi", 'echo "WHITESPACE RANGE: $WHITESPACE_RANGE"', - 'git diff --check "$WHITESPACE_RANGE"', + 'if git diff --name-only "$WHITESPACE_RANGE" | grep -qE \'(^|/)\\.gitattributes$\'; then', + 'echo "WHITESPACE FAIL: .gitattributes changed in checked range ($WHITESPACE_RANGE)"', + "exit 1", + "fi", + 'git -c core.attributesFile=/dev/null diff --check "$WHITESPACE_RANGE"', ] if whitespace_exec != expected_whitespace_exec: fail("whitespace guard must match the exact range-aware command sequence") @@ -1079,6 +1087,8 @@ if top_jobs_index is not None: fail("whitespace guard env must define VT_WS_BASE_SHA from GitHub event context") if whitespace_env.get("VT_WS_HEAD_SHA") != "${{ github.event.pull_request.head.sha || github.sha }}": fail("whitespace guard env must define VT_WS_HEAD_SHA from GitHub event context") + if whitespace_env.get("VT_WS_EVENT_NAME") != "${{ github.event_name }}": + fail("whitespace guard env must define VT_WS_EVENT_NAME from GitHub event context") if len(gate_steps) == 1: gate = gate_steps[0] diff --git a/tests/run-gates.sh b/tests/run-gates.sh index 80724f5..8307f8b 100644 --- a/tests/run-gates.sh +++ b/tests/run-gates.sh @@ -886,7 +886,7 @@ if ( cd "$WHITESPACE_REPO" && WS_BASE_SHA="$(git rev-parse HEAD~1)" && WS_HEAD_SHA="$(git rev-parse HEAD)" && - git diff --check "$WS_BASE_SHA..$WS_HEAD_SHA" >"$WHITESPACE_OUT" 2>&1 + git -c core.attributesFile=/dev/null diff --check "$WS_BASE_SHA..$WS_HEAD_SHA" >"$WHITESPACE_OUT" 2>&1 ); then fail "G-21 range-aware whitespace guard rejects committed whitespace" "range-aware diff unexpectedly passed" else @@ -913,13 +913,77 @@ if ( git add clean.txt && git commit -q -m "head-clean" && WS_HEAD_SHA="$(git rev-parse HEAD)" && - git diff --check "$WS_BASE_SHA..$WS_HEAD_SHA" >"$WHITESPACE_OUT" 2>&1 + git -c core.attributesFile=/dev/null diff --check "$WS_BASE_SHA..$WS_HEAD_SHA" >"$WHITESPACE_OUT" 2>&1 ); then pass "G-23 range-aware whitespace guard passes clean committed range" else fail "G-23 range-aware whitespace guard passes clean committed range" "$(tr '\n' ';' <"$WHITESPACE_OUT")" fi +WHITESPACE_PR_REPO="$WORK/whitespace-pr-merge-base-repo" +mkdir -p "$WHITESPACE_PR_REPO" +if ( + cd "$WHITESPACE_PR_REPO" && + git init -q && + git config user.name "verification-tools-gates" && + git config user.email "verification-tools@example.invalid" && + git config core.autocrlf false && + printf 'legacy \n' > inherited.txt && + git add inherited.txt && + git commit -q -m "branch-point-with-legacy-whitespace" && + git branch feature && + printf 'legacy\n' > inherited.txt && + git add inherited.txt && + git commit -q -m "main-cleans-legacy-whitespace" && + WS_BASE_SHA="$(git rev-parse HEAD)" && + printf '%s' "$WS_BASE_SHA" > .ws-base-sha && + git checkout -q feature && + printf 'feature\n' > feature.txt && + git add feature.txt && + git commit -q -m "feature-does-not-touch-legacy-whitespace" && + WS_HEAD_SHA="$(git rev-parse HEAD)" && + git -c core.attributesFile=/dev/null diff --check "$WS_BASE_SHA..$WS_HEAD_SHA" >"$WHITESPACE_OUT" 2>&1 +); then + fail "G-24 two-dot whitespace diff false-positives after base cleanup" "two-dot diff unexpectedly passed" +else + pass "G-24 two-dot whitespace diff false-positives after base cleanup" +fi + +if ( + cd "$WHITESPACE_PR_REPO" && + WS_BASE_SHA="$(cat .ws-base-sha)" && + WS_HEAD_SHA="$(git rev-parse HEAD)" && + git -c core.attributesFile=/dev/null diff --check "$WS_BASE_SHA...$WS_HEAD_SHA" >"$WHITESPACE_OUT" 2>&1 +); then + pass "G-25 triple-dot whitespace diff uses merge base for pull requests" +else + fail "G-25 triple-dot whitespace diff uses merge base for pull requests" "$(tr '\n' ';' <"$WHITESPACE_OUT")" +fi + +WHITESPACE_ATTR_REPO="$WORK/whitespace-attributes-repo" +mkdir -p "$WHITESPACE_ATTR_REPO" +if ( + cd "$WHITESPACE_ATTR_REPO" && + git init -q && + git config user.name "verification-tools-gates" && + git config user.email "verification-tools@example.invalid" && + git config core.autocrlf false && + printf 'clean\n' > tracked.txt && + git add tracked.txt && + git commit -q -m "base-clean" && + WS_BASE_SHA="$(git rev-parse HEAD)" && + printf '* -whitespace\n' > .gitattributes && + printf 'dirty \n' > tracked.txt && + git add .gitattributes tracked.txt && + git commit -q -m "head-relaxes-whitespace-attributes" && + WS_HEAD_SHA="$(git rev-parse HEAD)" && + git diff --name-only "$WS_BASE_SHA..$WS_HEAD_SHA" | grep -qE '(^|/)\.gitattributes$' +); then + pass "G-26 whitespace guard rejects checked-range .gitattributes changes" +else + fail "G-26 whitespace guard rejects checked-range .gitattributes changes" ".gitattributes change was not detected in the checked range" +fi + echo if [ "$FAILED" -eq 0 ]; then HEAD_SHA="$("$GIT_BIN" rev-parse HEAD 2>/dev/null || printf 'unknown')" From 6c70fa5ad7a1d870536ced0f9d674152265124e4 Mon Sep 17 00:00:00 2001 From: rene-founder Date: Tue, 7 Jul 2026 17:51:53 -0600 Subject: [PATCH 3/8] fix(ci): sync proof pins after whitespace hardening --- .github/workflows/ci.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4a95889..92e6392 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -212,12 +212,12 @@ jobs: - name: G-19 CI anti-theater (run-gates execution proof required) if: always() env: - VT_G19_EXPECTED_RUN_GATES_SHA: "0698b217eff2ac9933df31647b86bd77c8ce3d6f5457a1d98c1aea13bd9ca679" - VT_G19_EXPECTED_STRUCTURAL_CHECK_SHA: "19f3cb6147db94993a3f3f5e53e6dc91e5694c62d719d7b935fbde44f0b5a800" + VT_G19_EXPECTED_RUN_GATES_SHA: "f0bb2234e7b0751ce05c75a3a37c2d0c2dfd27a6254e1821320ac53940a1625a" + VT_G19_EXPECTED_STRUCTURAL_CHECK_SHA: "4636f9426ac79655130fa075e861f60b5bae96c30a5b7e1af58b5f977a5c2c8c" VT_G19_EXPECTED_WORKSPACE_HELPER_SHA: "e105de0bca00c509bfff6923515cccb9ab416013a20f17ff90a7159c52890a06" VT_G19_EXPECTED_VERIFY_PY_SHA: "abedd126eac0227dddad9267d8863d1b3b4921ef0bdc69301f59b9c9d708683b" VT_G19_EXPECTED_VERIFY_SH_SHA: "65694b5e0abe95f3322c45ab7c69b597d83121ec14001bdbba8e6e83f2224192" - VT_G19_EXPECTED_FIXTURE_MANIFEST_SHA: "ed002608e565b853c03f868097e684361e43b49db6ae75f3567b8ef75dfea8ed" + VT_G19_EXPECTED_FIXTURE_MANIFEST_SHA: "0b279bc36a9faafe09ef9f2acd7856cd3a9dbc623aea9a2008749f31bec858e8" VT_G19_PR_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} VT_G19_PR_BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before || github.sha }} VT_G19_CHECKOUT_SHA: ${{ github.sha }} From 45738eb5f4d75cfe5088f16f1d28f027bc6d3b78 Mon Sep 17 00:00:00 2001 From: rene-founder Date: Tue, 7 Jul 2026 18:01:50 -0600 Subject: [PATCH 4/8] fix(ci): align fixture manifest proof pin --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 92e6392..1be8205 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -217,7 +217,7 @@ jobs: VT_G19_EXPECTED_WORKSPACE_HELPER_SHA: "e105de0bca00c509bfff6923515cccb9ab416013a20f17ff90a7159c52890a06" VT_G19_EXPECTED_VERIFY_PY_SHA: "abedd126eac0227dddad9267d8863d1b3b4921ef0bdc69301f59b9c9d708683b" VT_G19_EXPECTED_VERIFY_SH_SHA: "65694b5e0abe95f3322c45ab7c69b597d83121ec14001bdbba8e6e83f2224192" - VT_G19_EXPECTED_FIXTURE_MANIFEST_SHA: "0b279bc36a9faafe09ef9f2acd7856cd3a9dbc623aea9a2008749f31bec858e8" + VT_G19_EXPECTED_FIXTURE_MANIFEST_SHA: "ed002608e565b853c03f868097e684361e43b49db6ae75f3567b8ef75dfea8ed" VT_G19_PR_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }} VT_G19_PR_BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before || github.sha }} VT_G19_CHECKOUT_SHA: ${{ github.sha }} From 8b98606e2812d1007395d2f9792b55b03231e92f Mon Sep 17 00:00:00 2001 From: rene-founder Date: Tue, 7 Jul 2026 19:10:18 -0600 Subject: [PATCH 5/8] fix(ci): harden whitespace guard edge cases --- .github/workflows/ci.yml | 14 +++- tests/g19-v2-structural-check.sh | 14 +++- tests/run-gates.sh | 115 +++++++++++++++++++++++++++++++ 3 files changed, 141 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1be8205..c9cf3d0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -92,10 +92,22 @@ jobs: WHITESPACE_RANGE="$VT_WS_BASE_SHA..$VT_WS_HEAD_SHA" fi echo "WHITESPACE RANGE: $WHITESPACE_RANGE" - if git diff --name-only "$WHITESPACE_RANGE" | grep -qE '(^|/)\.gitattributes$'; then + WHITESPACE_CHANGED_FILE=.vt-whitespace-changed-paths.txt + git diff --name-status --no-renames "$WHITESPACE_RANGE" > "$WHITESPACE_CHANGED_FILE" + WS_ATTR_CHANGED=0 + while IFS= read -r ws_line; do + [ -z "$ws_line" ] && continue + case "$ws_line" in + *".gitattributes") + WS_ATTR_CHANGED=1 + ;; + esac + done < "$WHITESPACE_CHANGED_FILE" + if [ "$WS_ATTR_CHANGED" -eq 1 ]; then echo "WHITESPACE FAIL: .gitattributes changed in checked range ($WHITESPACE_RANGE)" exit 1 fi + rm -f "$WHITESPACE_CHANGED_FILE" git -c core.attributesFile=/dev/null diff --check "$WHITESPACE_RANGE" - name: Run verification gate suite diff --git a/tests/g19-v2-structural-check.sh b/tests/g19-v2-structural-check.sh index c57d46d..ab9f181 100644 --- a/tests/g19-v2-structural-check.sh +++ b/tests/g19-v2-structural-check.sh @@ -1075,10 +1075,22 @@ if top_jobs_index is not None: 'WHITESPACE_RANGE="$VT_WS_BASE_SHA..$VT_WS_HEAD_SHA"', "fi", 'echo "WHITESPACE RANGE: $WHITESPACE_RANGE"', - 'if git diff --name-only "$WHITESPACE_RANGE" | grep -qE \'(^|/)\\.gitattributes$\'; then', + 'WHITESPACE_CHANGED_FILE=.vt-whitespace-changed-paths.txt', + 'git diff --name-status --no-renames "$WHITESPACE_RANGE" > "$WHITESPACE_CHANGED_FILE"', + 'WS_ATTR_CHANGED=0', + 'while IFS= read -r ws_line; do', + '[ -z "$ws_line" ] && continue', + 'case "$ws_line" in', + '*".gitattributes")', + 'WS_ATTR_CHANGED=1', + ';;', + 'esac', + 'done < "$WHITESPACE_CHANGED_FILE"', + 'if [ "$WS_ATTR_CHANGED" -eq 1 ]; then', 'echo "WHITESPACE FAIL: .gitattributes changed in checked range ($WHITESPACE_RANGE)"', "exit 1", "fi", + 'rm -f "$WHITESPACE_CHANGED_FILE"', 'git -c core.attributesFile=/dev/null diff --check "$WHITESPACE_RANGE"', ] if whitespace_exec != expected_whitespace_exec: diff --git a/tests/run-gates.sh b/tests/run-gates.sh index 8307f8b..610fce1 100644 --- a/tests/run-gates.sh +++ b/tests/run-gates.sh @@ -984,6 +984,121 @@ else fail "G-26 whitespace guard rejects checked-range .gitattributes changes" ".gitattributes change was not detected in the checked range" fi +WHITESPACE_PIPEFAIL_REPO="$WORK/whitespace-attributes-pipefail-repo" +mkdir -p "$WHITESPACE_PIPEFAIL_REPO" +if ( + cd "$WHITESPACE_PIPEFAIL_REPO" && + git init -q && + git config user.name "verification-tools-gates" && + git config user.email "verification-tools@example.invalid" && + git config core.autocrlf false && + printf 'clean\n' > tracked.txt && + git add tracked.txt && + git commit -q -m "base-clean" && + WS_BASE_SHA="$(git rev-parse HEAD)" && + printf '* -whitespace\n' > .gitattributes && + i=0 && + while [ "$i" -lt 512 ]; do + printf 'filler %s\n' "$i" > "$(printf 'z%03d.txt' "$i")" && + i=$((i + 1)) + done && + git add .gitattributes z*.txt && + git commit -q -m "head-many-files-with-attributes" && + WS_HEAD_SHA="$(git rev-parse HEAD)" && + set -o pipefail && + WHITESPACE_CHANGED_PATHS="$(git diff --name-status --no-renames "$WS_BASE_SHA..$WS_HEAD_SHA")" && + WS_ATTR_CHANGED=0 && + while IFS= read -r ws_line; do + [ -z "$ws_line" ] && continue + ws_path="${ws_line##* }" + case "$ws_path" in + .gitattributes|*/.gitattributes) + WS_ATTR_CHANGED=1 + ;; + esac + done < .gitattributes && + printf 'clean\n' > tracked.txt && + git add .gitattributes tracked.txt && + git commit -q -m "base-with-gitattributes" && + WS_BASE_SHA="$(git rev-parse HEAD)" && + git mv .gitattributes attrs && + printf '\tindented\n' > tracked.txt && + git add attrs tracked.txt && + git commit -q -m "rename-away-gitattributes" && + WS_HEAD_SHA="$(git rev-parse HEAD)" && + ! git diff --name-only "$WS_BASE_SHA..$WS_HEAD_SHA" | grep -qE '(^|/)\.gitattributes$' +); then + pass "G-29 name-only .gitattributes detection misses rename-away sources" +else + fail "G-29 name-only .gitattributes detection misses rename-away sources" "name-only unexpectedly detected renamed-away .gitattributes" +fi + +if ( + cd "$WHITESPACE_RENAME_REPO" && + WS_BASE_SHA="$(git rev-parse HEAD~1)" && + WS_HEAD_SHA="$(git rev-parse HEAD)" && + WHITESPACE_CHANGED_PATHS="$(git diff --name-status --no-renames "$WS_BASE_SHA..$WS_HEAD_SHA")" && + WS_ATTR_CHANGED=0 && + while IFS= read -r ws_line; do + [ -z "$ws_line" ] && continue + ws_path="${ws_line##* }" + case "$ws_path" in + .gitattributes|*/.gitattributes) + WS_ATTR_CHANGED=1 + ;; + esac + done </dev/null || printf 'unknown')" From 478b79bcb709d8892290f9abba6e81bb60dabf6c Mon Sep 17 00:00:00 2001 From: rene-founder Date: Tue, 7 Jul 2026 19:20:23 -0600 Subject: [PATCH 6/8] fix(ci): align proof pins for whitespace hardening --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c9cf3d0..c286342 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -224,8 +224,8 @@ jobs: - name: G-19 CI anti-theater (run-gates execution proof required) if: always() env: - VT_G19_EXPECTED_RUN_GATES_SHA: "f0bb2234e7b0751ce05c75a3a37c2d0c2dfd27a6254e1821320ac53940a1625a" - VT_G19_EXPECTED_STRUCTURAL_CHECK_SHA: "4636f9426ac79655130fa075e861f60b5bae96c30a5b7e1af58b5f977a5c2c8c" + VT_G19_EXPECTED_RUN_GATES_SHA: "da27bc62e84963dbcd9f799de4d51c4561d98a3a2515383e0cc88407ca11d257" + VT_G19_EXPECTED_STRUCTURAL_CHECK_SHA: "2ef5940f78d430329fcb3f0533014ccefb5fb3691ca4655677366551ad292a30" VT_G19_EXPECTED_WORKSPACE_HELPER_SHA: "e105de0bca00c509bfff6923515cccb9ab416013a20f17ff90a7159c52890a06" VT_G19_EXPECTED_VERIFY_PY_SHA: "abedd126eac0227dddad9267d8863d1b3b4921ef0bdc69301f59b9c9d708683b" VT_G19_EXPECTED_VERIFY_SH_SHA: "65694b5e0abe95f3322c45ab7c69b597d83121ec14001bdbba8e6e83f2224192" From bb8646f40c902c38de57739269582d068eaa6635 Mon Sep 17 00:00:00 2001 From: rene-founder Date: Tue, 7 Jul 2026 20:03:04 -0600 Subject: [PATCH 7/8] fix(ci): narrow .gitattributes path matching --- .github/workflows/ci.yml | 5 +++-- tests/g19-v2-structural-check.sh | 5 +++-- tests/run-gates.sh | 38 ++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c286342..c57a8ee 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -97,8 +97,9 @@ jobs: WS_ATTR_CHANGED=0 while IFS= read -r ws_line; do [ -z "$ws_line" ] && continue - case "$ws_line" in - *".gitattributes") + ws_path="${ws_line#*$'\t'}" + case "$ws_path" in + .gitattributes|*/.gitattributes) WS_ATTR_CHANGED=1 ;; esac diff --git a/tests/g19-v2-structural-check.sh b/tests/g19-v2-structural-check.sh index ab9f181..e189aae 100644 --- a/tests/g19-v2-structural-check.sh +++ b/tests/g19-v2-structural-check.sh @@ -1080,8 +1080,9 @@ if top_jobs_index is not None: 'WS_ATTR_CHANGED=0', 'while IFS= read -r ws_line; do', '[ -z "$ws_line" ] && continue', - 'case "$ws_line" in', - '*".gitattributes")', + "ws_path=\"${ws_line#*$'\\t'}\"", + 'case "$ws_path" in', + '.gitattributes|*/.gitattributes)', 'WS_ATTR_CHANGED=1', ';;', 'esac', diff --git a/tests/run-gates.sh b/tests/run-gates.sh index 610fce1..37f8315 100644 --- a/tests/run-gates.sh +++ b/tests/run-gates.sh @@ -1099,6 +1099,44 @@ else fail "G-30 status-based .gitattributes detection catches rename-away sources" "status-based detection missed renamed-away .gitattributes" fi +WHITESPACE_SUFFIX_REPO="$WORK/whitespace-suffix-path-repo" +mkdir -p "$WHITESPACE_SUFFIX_REPO" +if ( + cd "$WHITESPACE_SUFFIX_REPO" && + git init -q && + git config user.name "verification-tools-gates" && + git config user.email "verification-tools@example.invalid" && + git config core.autocrlf false && + mkdir -p docs && + printf 'clean\n' > tracked.txt && + git add tracked.txt && + git commit -q -m "base-clean" && + WS_BASE_SHA="$(git rev-parse HEAD)" && + printf 'fixture only\n' > docs/foo.gitattributes && + git add docs/foo.gitattributes && + git commit -q -m "head-adds-suffix-only-file" && + WS_HEAD_SHA="$(git rev-parse HEAD)" && + WHITESPACE_CHANGED_PATHS="$(git diff --name-status --no-renames "$WS_BASE_SHA..$WS_HEAD_SHA")" && + WS_ATTR_CHANGED=0 && + while IFS= read -r ws_line; do + [ -z "$ws_line" ] && continue + ws_path="${ws_line##* }" + case "$ws_path" in + .gitattributes|*/.gitattributes) + WS_ATTR_CHANGED=1 + ;; + esac + done </dev/null || printf 'unknown')" From 4e93294a860c922a1018716d2e88f08d60b5a7ed Mon Sep 17 00:00:00 2001 From: rene-founder Date: Tue, 7 Jul 2026 20:17:07 -0600 Subject: [PATCH 8/8] fix(ci): refresh proof pins for whitespace guard --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c57a8ee..c3cc20c 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -225,8 +225,8 @@ jobs: - name: G-19 CI anti-theater (run-gates execution proof required) if: always() env: - VT_G19_EXPECTED_RUN_GATES_SHA: "da27bc62e84963dbcd9f799de4d51c4561d98a3a2515383e0cc88407ca11d257" - VT_G19_EXPECTED_STRUCTURAL_CHECK_SHA: "2ef5940f78d430329fcb3f0533014ccefb5fb3691ca4655677366551ad292a30" + VT_G19_EXPECTED_RUN_GATES_SHA: "d61172ad3ebfa6705738d54f58a464d3aa508910cd96a37e59a23e81619ba678" + VT_G19_EXPECTED_STRUCTURAL_CHECK_SHA: "121d7484172ed7c931d0fd57101d972a9eca9f73d41058b4ca04183893acb93d" VT_G19_EXPECTED_WORKSPACE_HELPER_SHA: "e105de0bca00c509bfff6923515cccb9ab416013a20f17ff90a7159c52890a06" VT_G19_EXPECTED_VERIFY_PY_SHA: "abedd126eac0227dddad9267d8863d1b3b4921ef0bdc69301f59b9c9d708683b" VT_G19_EXPECTED_VERIFY_SH_SHA: "65694b5e0abe95f3322c45ab7c69b597d83121ec14001bdbba8e6e83f2224192"