From 4343215e71217d0007c3663d424436874e74d0a8 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Oct 2026 05:04:40 +0000 Subject: [PATCH 1/4] feat(governance): run the fleet report-backs on every PR and feed them to the grid The Hypatia verdict grid on every PR here listed seven enrolled gitbot-fleet members, all orange: "enrolled, but no report-back was received". Nothing produced the envelopes. The producer now exists (hyperpolymath/gitbot-fleet#626); this wires it in. * hypatia-scan.yml: a `fleet` job calling gitbot-fleet's fleet-report-back-reusable.yml at an immutable commit (same owner, so the Actions policy allows it); `hypatia` now runs after it under always(), still never after a failed contract. * hypatia-scan-reusable.yml (fork-local): fork delta 4, the `report-backs-artifact` input and a tolerant download step placing the envelopes in .gitbot-fleet/report-backs/ before "Render the verdict grid". Upstream standards carries the same change after this copy's source pin. * scripts/check-governance-contract.sh guards the wiring (pinned producer, ordering, tolerant download, hypatia needs both jobs); five new mutations in scripts/tests/test-governance-contract.sh are rejected. * .gitbot-fleet/report-backs/README.md says how the files arrive and that none is ever committed. Expected on the next PR grid, from a local run of the same adapters on this tree: rhodibot, seambot, finishingbot, accessibilitybot red (advisory), panicbot and glambot yellow, echidnabot orange with the stated reason (no ECHIDNA engine configured). Rows are advisory; none sets the lane state. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01J4QvDjqj5DHRRCrDiGeTmq --- .gitbot-fleet/report-backs/README.md | 25 +++++++++------- .github/workflows/hypatia-scan-reusable.yml | 29 ++++++++++++++++++- .github/workflows/hypatia-scan.yml | 17 ++++++++++- scripts/check-governance-contract.sh | 32 ++++++++++++++++++++- scripts/tests/test-governance-contract.sh | 9 +++++- 5 files changed, 98 insertions(+), 14 deletions(-) mode change 100755 => 100644 scripts/check-governance-contract.sh diff --git a/.gitbot-fleet/report-backs/README.md b/.gitbot-fleet/report-backs/README.md index 32b48064..4647d911 100644 --- a/.gitbot-fleet/report-backs/README.md +++ b/.gitbot-fleet/report-backs/README.md @@ -7,10 +7,10 @@ should match the key in `.gitbot-fleet/enrolment.json` (`rhodibot`, `seambot`, . The Hypatia verdict grid reads this directory (`.github/workflows/hypatia-scan-reusable.yml` passes it to -`scripts/verdict-grid.sh --report-backs`), so a report-back written here is the -only way a member's row stops being orange. The envelope is the one in +`scripts/verdict-grid.sh --report-backs`), so a report-back here is the only +way a member's row stops being orange. The envelope is the one in `docs/VERDICT-GRID-SPEC.adoc` of -[hyperpolymath/standards](https://github.com/hyperpolymath/standards): +[metadatastician/standards](https://github.com/metadatastician/standards): ```json { "reporter": "rhodibot", @@ -29,12 +29,17 @@ required unless the member ran, and `not_applicable` renders grey. `severity` uses the fleet's own words (`error`, `warning`, `info`, `suggestion`) or the scanner words (`critical`, `high`, `medium`, `low`); both normalise. -The directory exists because this repository's deed declares it -(`MetaManifold-WebUI_chora.deed`, `:report-backs ".gitbot-fleet/report-backs/"`) -and a declared path that does not exist is a dangling reference. It is empty -on purpose: no member has reported back yet, and the grid says so - an -enrolled member with no report-back renders **orange** ("enrolled, but no -report-back was received"), never green. Do not add a placeholder envelope to -turn a row green; only a real run may. +**How the files get here.** The `fleet` job of `.github/workflows/hypatia-scan.yml` +(gitbot-fleet's `fleet-report-back-reusable.yml`) runs the enrolled adapters on +the PR head and uploads the envelopes as the `fleet-report-backs` artifact; +the fork-local Hypatia reusable downloads that artifact into this directory +just before it renders the grid (fork delta 4). Nothing is ever committed here: +an envelope is evidence about one commit, and a committed one would read as +current when it is stale. The directory exists because this repository's deed +declares it (`MetaManifold-WebUI_chora.deed`, +`:report-backs ".gitbot-fleet/report-backs/"`), and it is empty in git on +purpose. An enrolled member with no report-back renders **orange** ("enrolled, +but no report-back was received"), never green; do not add a placeholder +envelope to turn a row green. Only a real run may. Only files matching `*.json` are read, so this README is inert. diff --git a/.github/workflows/hypatia-scan-reusable.yml b/.github/workflows/hypatia-scan-reusable.yml index d89e0d5b..3c44f794 100644 --- a/.github/workflows/hypatia-scan-reusable.yml +++ b/.github/workflows/hypatia-scan-reusable.yml @@ -4,7 +4,7 @@ # # Fork-local copy of metadatastician/standards/.github/workflows/ # hypatia-scan-reusable.yml at 01c9b0c9f2646ebea0b30a9588462642ac94aee9. -# Keep that source pin when updating this file and reapply the three fork deltas: +# Keep that source pin when updating this file and reapply the four fork deltas: # 1. Do not upload SARIF for pull_request events. This lane is advisory on # PRs; uploading error-level results creates a separate, blocking # "Code scanning results / Hypatia" check even when the scan job passes. @@ -15,6 +15,12 @@ # 3. Filter only upstream-inherited TypeScript paths from the effective # findings, validate the raw/effective JSON with pinned ijson-jcs, and keep # raw scanner outputs plus fork-only paths/rules. +# 4. Download the gitbot-fleet report-backs (artifact `fleet-report-backs`, +# produced by the `fleet` job of hypatia-scan.yml) into +# .gitbot-fleet/report-backs/ before the verdict grid renders, so each +# enrolled member's row carries evidence instead of "enrolled, but no +# report-back was received". Upstream standards added the same step and +# input after 01c9b0c9; this copy carries them ahead of its source pin. # scripts/check-governance-contract.sh guards these deltas. # # Hypatia Neurosymbolic CI/CD Security Scan — Reusable Workflow @@ -28,6 +34,16 @@ on: type: boolean required: false default: false + report-backs-artifact: + description: >- + Name of an artifact, uploaded earlier in the same run, holding the + gitbot-fleet report-back envelopes (one .json each). Downloaded + into .gitbot-fleet/report-backs/ before the verdict grid renders; a + missing artifact is tolerated and leaves the enrolled rows orange. Set + to an empty string to skip the download. + type: string + required: false + default: "fleet-report-backs" hypatia-ref: description: >- Hypatia commit to build the scanner from. Defaults to a vetted SHA so a @@ -728,6 +744,17 @@ jobs: | last | .body // ""' \ > "$RUNNER_TEMP/grid-previous.md" || : > "$RUNNER_TEMP/grid-previous.md" + # Fork delta 4: fleet report-backs. Produced per run by the `fleet` job of + # hypatia-scan.yml and reaching this job only as an artifact; never + # committed. continue-on-error: no artifact leaves the enrolled rows orange. + - name: Download the fleet report-backs + if: ${{ always() && inputs.report-backs-artifact != '' }} + continue-on-error: true + uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2 + with: + name: ${{ inputs.report-backs-artifact }} + path: .gitbot-fleet/report-backs + - name: Render the verdict grid id: grid if: ${{ always() }} diff --git a/.github/workflows/hypatia-scan.yml b/.github/workflows/hypatia-scan.yml index 83f21656..859a4dc8 100644 --- a/.github/workflows/hypatia-scan.yml +++ b/.github/workflows/hypatia-scan.yml @@ -76,9 +76,24 @@ jobs: require-files: true fail-on-indeterminate: true + # Fleet report-backs, produced before the Hypatia lane renders so each enrolled + # gitbot-fleet member's row (.gitbot-fleet/enrolment.json) carries evidence + # instead of "enrolled, but no report-back was received". Same owner + # (hyperpolymath), so the reusable is allowed by this repository's Actions + # policy; scripts/check-governance-contract.sh guards the pin and the wiring. + fleet: + name: Fleet report-backs + needs: [workflow-contract] + uses: hyperpolymath/gitbot-fleet/.github/workflows/fleet-report-back-reusable.yml@68858e0951d34ba48bb5229dfee67edf9766783b + permissions: + contents: read + hypatia: name: Hypatia verdict grid - needs: [workflow-contract] + # After the fleet job whatever its result (a dead fleet job renders as orange + # fleet rows, never as a missing lane), but never after a failed contract. + needs: [workflow-contract, fleet] + if: ${{ always() && needs.workflow-contract.result == 'success' }} # Fork-local so advisory PR scans do not upload error-level SARIF and # create a second blocking check. The source pin and deltas are documented # in the called file and checked by scripts/check-governance-contract.sh. diff --git a/scripts/check-governance-contract.sh b/scripts/check-governance-contract.sh old mode 100755 new mode 100644 index f0064529..cd9155df --- a/scripts/check-governance-contract.sh +++ b/scripts/check-governance-contract.sh @@ -126,7 +126,37 @@ not_contains "$caller" 'uses: metadatastician/standards/.github/workflows/hypati not_contains "$caller" 'uses: hyperpolymath/standards/.github/workflows/hypatia-scan-reusable.yml@' \ 'the governance lane cannot bypass the fork guard through the pre-transfer name' contains "$caller" 'needs: [workflow-contract]' \ - 'this contract runs before the Hypatia scan' + 'this contract runs before the fleet job' +contains "$caller" 'needs: [workflow-contract, fleet]' \ + 'the Hypatia scan runs after both the contract and the fleet report-backs' +contains "$caller" "if: \${{ always() && needs.workflow-contract.result == 'success' }}" \ + 'a dead fleet job still lets the Hypatia lane render, a failed contract does not' +# Fork delta 4: the fleet report-backs. The producer is pinned to a gitbot-fleet +# commit (same owner, so the Actions policy allows the reusable); the consumer +# step must sit before the grid renders, or the rows stay orange with the +# artifact sitting unread in the run. +if grep -Eq '^ uses: hyperpolymath/gitbot-fleet/\.github/workflows/fleet-report-back-reusable\.yml@[0-9a-f]{40}$' <<<"$caller"; then + pass 'the fleet report-backs come from gitbot-fleet at an immutable commit' +else + fail 'the fleet report-backs must come from hyperpolymath/gitbot-fleet pinned to a 40-hex commit' +fi +if fleet_dl="$(step_block "$REUSABLE" 'Download the fleet report-backs')"; then + contains "$fleet_dl" 'path: .gitbot-fleet/report-backs' \ + 'the report-backs land where the renderer reads them' + contains "$fleet_dl" 'continue-on-error: true' \ + 'a missing report-backs artifact leaves rows orange instead of killing the lane' + contains "$fleet_dl" 'name: ${{ inputs.report-backs-artifact }}' \ + 'the artifact name is the caller-visible input, not a literal' + dl_line=$(grep -nF -- '- name: Download the fleet report-backs' "$REUSABLE" | cut -d: -f1 | head -n1) + render_line=$(grep -nF -- '- name: Render the verdict grid' "$REUSABLE" | cut -d: -f1 | head -n1) + if [ -n "$dl_line" ] && [ -n "$render_line" ] && [ "$dl_line" -lt "$render_line" ]; then + pass 'the report-backs are downloaded before the verdict grid renders' + else + fail 'the report-backs download must precede the verdict grid render step' + fi +else + fail 'the fleet report-backs download step exists' +fi if grep -Eq '^ pull_request:$' <<<"$caller"; then pass 'governance still runs for pull requests' else diff --git a/scripts/tests/test-governance-contract.sh b/scripts/tests/test-governance-contract.sh index 5ce57161..e12dee73 100644 --- a/scripts/tests/test-governance-contract.sh +++ b/scripts/tests/test-governance-contract.sh @@ -14,7 +14,7 @@ reset_fixture() { } reset_fixture bash "$fixture/scripts/check-governance-contract.sh" > "$fixture/log" 2>&1 -for mutation in allowlist-addition baseline-zero-gate baseline-operator-gate baseline-grouping-gate sarif upstream-exception effective-grid jcs-manifest jcs-canonicalizer raw-ijson k9-pin k9-require k9-indeterminate k9-verdict scanner-advisory scanner-secrets scanner-trigger scorecard-trigger stale-standards-owner scorecard-owner deed-remote scorecard-remote deed-source-sha scorecard-source-sha scorecard-no-upload scorecard-quiet-reconcile cross-owner-reusable; do +for mutation in allowlist-addition baseline-zero-gate baseline-operator-gate baseline-grouping-gate sarif upstream-exception effective-grid jcs-manifest jcs-canonicalizer raw-ijson k9-pin k9-require k9-indeterminate k9-verdict scanner-advisory scanner-secrets scanner-trigger scorecard-trigger stale-standards-owner scorecard-owner deed-remote scorecard-remote deed-source-sha scorecard-source-sha scorecard-no-upload scorecard-quiet-reconcile cross-owner-reusable fleet-unpinned fleet-before-contract fleet-dropped-download fleet-download-after-render fleet-download-fatal; do reset_fixture case "$mutation" in allowlist-addition) echo 'frontend/src/fork-only.ts' >> "$fixture/config/hypatia/upstream-typescript-allowlist.txt" ;; @@ -49,6 +49,13 @@ for mutation in allowlist-addition baseline-zero-gate baseline-operator-gate bas deed-remote) sed -i 's|uses: ./.github/workflows/deed-conformance-reusable.yml|uses: metadatastician/standards/.github/workflows/deed-conformance.yml@75bd5994470661a099df562d6d5ad2fe744dbd66|' "$fixture/.github/workflows/hypatia-scan.yml" ;; scorecard-remote) sed -i 's|uses: ./.github/workflows/scorecard-reusable.yml|uses: metadatastician/standards/.github/workflows/scorecard-reusable.yml@c6d78606f3928c22c3b0bdc6f5f5b47b96e05a5d|' "$fixture/.github/workflows/scorecard.yml" ;; cross-owner-reusable) sed -i 's|uses: ./.github/workflows/k9-conformance-reusable.yml|uses: someone/else/.github/workflows/k9.yml@main|' "$fixture/.github/workflows/hypatia-scan.yml" ;; + # Fork delta 4: the fleet report-backs must come from an immutable gitbot-fleet + # commit, be downloaded before the grid renders, and never kill the lane. + fleet-unpinned) sed -i -E 's|(fleet-report-back-reusable\.yml)@[0-9a-f]{40}|\1@main|' "$fixture/.github/workflows/hypatia-scan.yml" ;; + fleet-before-contract) sed -i 's|needs: \[workflow-contract, fleet\]|needs: [fleet]|' "$fixture/.github/workflows/hypatia-scan.yml" ;; + fleet-dropped-download) sed -i '/^ - name: Download the fleet report-backs$/,/^ path: .gitbot-fleet\/report-backs$/d' "$fixture/.github/workflows/hypatia-scan-reusable.yml" ;; + fleet-download-after-render) awk '/^ - name: Download the fleet report-backs$/{hold=1} hold && /^ - name: Render the verdict grid$/{hold=0; print; printf "%s", buf; buf=""; next} hold{buf=buf $0 "\n"; next} {print}' "$fixture/.github/workflows/hypatia-scan-reusable.yml" > "$fixture/reordered.yml" && mv "$fixture/reordered.yml" "$fixture/.github/workflows/hypatia-scan-reusable.yml" ;; + fleet-download-fatal) sed -i '/^ - name: Download the fleet report-backs$/,/^ path: .gitbot-fleet\/report-backs$/{/continue-on-error: true/d}' "$fixture/.github/workflows/hypatia-scan-reusable.yml" ;; # A copy is only a copy while it still comes from the audited commit and # keeps the guards whose absence froze code scanning upstream. deed-source-sha) sed -i 's@75bd5994470661a099df562d6d5ad2fe744dbd66@main@' "$fixture/.github/workflows/deed-conformance-reusable.yml" ;; From 068f52da225464059d8b575c54a583c02b2e39d2 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Oct 2026 05:13:49 +0000 Subject: [PATCH 2/4] fix(governance): pin the fleet reusable to the commit with GitHub-owned, SHA-pinned actions The first run died at plan time with zero jobs: the reusable at 68858e09 used tag-pinned third-party actions this repository's Actions policy refuses. gitbot-fleet 4c9ea43d uses only GitHub-owned, SHA-pinned actions and no caller-chosen ref. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01J4QvDjqj5DHRRCrDiGeTmq --- .github/workflows/hypatia-scan.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/hypatia-scan.yml b/.github/workflows/hypatia-scan.yml index 859a4dc8..ae5edd3e 100644 --- a/.github/workflows/hypatia-scan.yml +++ b/.github/workflows/hypatia-scan.yml @@ -84,7 +84,7 @@ jobs: fleet: name: Fleet report-backs needs: [workflow-contract] - uses: hyperpolymath/gitbot-fleet/.github/workflows/fleet-report-back-reusable.yml@68858e0951d34ba48bb5229dfee67edf9766783b + uses: hyperpolymath/gitbot-fleet/.github/workflows/fleet-report-back-reusable.yml@4c9ea43d7ff8fc9209165d87845a6faea04c35d3 permissions: contents: read From e2e684e441f7fa1aeba1dff8a78f4c2b9bf1d66e Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Oct 2026 05:20:56 +0000 Subject: [PATCH 3/4] fix(governance): clear checked-out report-backs before the artifact download CodeRabbit on #296: the download tolerates a missing artifact, and .gitignore un-ignores .gitbot-fleet/**, so a committed *.json in .gitbot-fleet/report-backs/ could reach the renderer as evidence. The lane now deletes any *.json there before downloading; the README is not *.json and stays. The contract checks the step and its position; one new mutation. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01J4QvDjqj5DHRRCrDiGeTmq --- .github/workflows/hypatia-scan-reusable.yml | 9 +++++++++ scripts/check-governance-contract.sh | 13 +++++++++++++ scripts/tests/test-governance-contract.sh | 3 ++- 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/.github/workflows/hypatia-scan-reusable.yml b/.github/workflows/hypatia-scan-reusable.yml index 3c44f794..bef64c99 100644 --- a/.github/workflows/hypatia-scan-reusable.yml +++ b/.github/workflows/hypatia-scan-reusable.yml @@ -747,6 +747,15 @@ jobs: # Fork delta 4: fleet report-backs. Produced per run by the `fleet` job of # hypatia-scan.yml and reaching this job only as an artifact; never # committed. continue-on-error: no artifact leaves the enrolled rows orange. + # Nothing committed may pose as evidence: only files the artifact supplies + # are read. The committed README.md is not *.json and is left alone. + - name: Clear any checked-out fleet report-backs + if: ${{ always() }} + run: | + set -euo pipefail + mkdir -p .gitbot-fleet/report-backs + find .gitbot-fleet/report-backs -maxdepth 1 -name '*.json' -type f -print -delete + - name: Download the fleet report-backs if: ${{ always() && inputs.report-backs-artifact != '' }} continue-on-error: true diff --git a/scripts/check-governance-contract.sh b/scripts/check-governance-contract.sh index cd9155df..61ae7c5e 100644 --- a/scripts/check-governance-contract.sh +++ b/scripts/check-governance-contract.sh @@ -140,6 +140,19 @@ if grep -Eq '^ uses: hyperpolymath/gitbot-fleet/\.github/workflows/fleet-repo else fail 'the fleet report-backs must come from hyperpolymath/gitbot-fleet pinned to a 40-hex commit' fi +if fleet_clear="$(step_block "$REUSABLE" 'Clear any checked-out fleet report-backs')"; then + contains "$fleet_clear" "find .gitbot-fleet/report-backs -maxdepth 1 -name '*.json' -type f -print -delete" \ + 'a committed envelope can never pose as report-back evidence' + clear_line=$(grep -nF -- '- name: Clear any checked-out fleet report-backs' "$REUSABLE" | cut -d: -f1 | head -n1) + dl_line0=$(grep -nF -- '- name: Download the fleet report-backs' "$REUSABLE" | cut -d: -f1 | head -n1) + if [ -n "$clear_line" ] && [ -n "$dl_line0" ] && [ "$clear_line" -lt "$dl_line0" ]; then + pass 'the checked-out report-backs are cleared before the artifact is downloaded' + else + fail 'the report-backs clear step must precede the download' + fi +else + fail 'the fleet report-backs clear step exists' +fi if fleet_dl="$(step_block "$REUSABLE" 'Download the fleet report-backs')"; then contains "$fleet_dl" 'path: .gitbot-fleet/report-backs' \ 'the report-backs land where the renderer reads them' diff --git a/scripts/tests/test-governance-contract.sh b/scripts/tests/test-governance-contract.sh index e12dee73..37d62fbf 100644 --- a/scripts/tests/test-governance-contract.sh +++ b/scripts/tests/test-governance-contract.sh @@ -14,7 +14,7 @@ reset_fixture() { } reset_fixture bash "$fixture/scripts/check-governance-contract.sh" > "$fixture/log" 2>&1 -for mutation in allowlist-addition baseline-zero-gate baseline-operator-gate baseline-grouping-gate sarif upstream-exception effective-grid jcs-manifest jcs-canonicalizer raw-ijson k9-pin k9-require k9-indeterminate k9-verdict scanner-advisory scanner-secrets scanner-trigger scorecard-trigger stale-standards-owner scorecard-owner deed-remote scorecard-remote deed-source-sha scorecard-source-sha scorecard-no-upload scorecard-quiet-reconcile cross-owner-reusable fleet-unpinned fleet-before-contract fleet-dropped-download fleet-download-after-render fleet-download-fatal; do +for mutation in allowlist-addition baseline-zero-gate baseline-operator-gate baseline-grouping-gate sarif upstream-exception effective-grid jcs-manifest jcs-canonicalizer raw-ijson k9-pin k9-require k9-indeterminate k9-verdict scanner-advisory scanner-secrets scanner-trigger scorecard-trigger stale-standards-owner scorecard-owner deed-remote scorecard-remote deed-source-sha scorecard-source-sha scorecard-no-upload scorecard-quiet-reconcile cross-owner-reusable fleet-unpinned fleet-before-contract fleet-dropped-download fleet-download-after-render fleet-download-fatal fleet-committed-evidence; do reset_fixture case "$mutation" in allowlist-addition) echo 'frontend/src/fork-only.ts' >> "$fixture/config/hypatia/upstream-typescript-allowlist.txt" ;; @@ -56,6 +56,7 @@ for mutation in allowlist-addition baseline-zero-gate baseline-operator-gate bas fleet-dropped-download) sed -i '/^ - name: Download the fleet report-backs$/,/^ path: .gitbot-fleet\/report-backs$/d' "$fixture/.github/workflows/hypatia-scan-reusable.yml" ;; fleet-download-after-render) awk '/^ - name: Download the fleet report-backs$/{hold=1} hold && /^ - name: Render the verdict grid$/{hold=0; print; printf "%s", buf; buf=""; next} hold{buf=buf $0 "\n"; next} {print}' "$fixture/.github/workflows/hypatia-scan-reusable.yml" > "$fixture/reordered.yml" && mv "$fixture/reordered.yml" "$fixture/.github/workflows/hypatia-scan-reusable.yml" ;; fleet-download-fatal) sed -i '/^ - name: Download the fleet report-backs$/,/^ path: .gitbot-fleet\/report-backs$/{/continue-on-error: true/d}' "$fixture/.github/workflows/hypatia-scan-reusable.yml" ;; + fleet-committed-evidence) sed -i '/^ - name: Clear any checked-out fleet report-backs$/,/-print -delete$/d' "$fixture/.github/workflows/hypatia-scan-reusable.yml" ;; # A copy is only a copy while it still comes from the audited commit and # keeps the guards whose absence froze code scanning upstream. deed-source-sha) sed -i 's@75bd5994470661a099df562d6d5ad2fe744dbd66@main@' "$fixture/.github/workflows/deed-conformance-reusable.yml" ;; From 37c2d3ef47823b77036156f36f71cef577c91aa5 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 27 Oct 2026 05:26:38 +0000 Subject: [PATCH 4/4] fix(governance): no Hypatia lane in a superseded run; repin the fleet reusable Codex on #296: with cancel-in-progress, always() let the Hypatia job (and the verdict job behind it) start in a run a newer push had cancelled, so a stale grid could overwrite the newer comment. The job now runs under !cancelled() with the contract still required; a failed fleet job still lets it render. Contract check and mutation updated. The fleet reusable moves to e85b71bf, which keeps the fleet checkout out of the scanned tree (walk-based adapters were scanning the fleet's own source) and reports rhodibot's relocated files at the path where they were found. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01J4QvDjqj5DHRRCrDiGeTmq --- .github/workflows/hypatia-scan.yml | 4 ++-- scripts/check-governance-contract.sh | 4 ++-- scripts/tests/test-governance-contract.sh | 3 ++- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/hypatia-scan.yml b/.github/workflows/hypatia-scan.yml index ae5edd3e..1d700e18 100644 --- a/.github/workflows/hypatia-scan.yml +++ b/.github/workflows/hypatia-scan.yml @@ -84,7 +84,7 @@ jobs: fleet: name: Fleet report-backs needs: [workflow-contract] - uses: hyperpolymath/gitbot-fleet/.github/workflows/fleet-report-back-reusable.yml@4c9ea43d7ff8fc9209165d87845a6faea04c35d3 + uses: hyperpolymath/gitbot-fleet/.github/workflows/fleet-report-back-reusable.yml@e85b71bf139a61f956c7e0717a2912cd6e347cc5 permissions: contents: read @@ -93,7 +93,7 @@ jobs: # After the fleet job whatever its result (a dead fleet job renders as orange # fleet rows, never as a missing lane), but never after a failed contract. needs: [workflow-contract, fleet] - if: ${{ always() && needs.workflow-contract.result == 'success' }} + if: ${{ !cancelled() && needs.workflow-contract.result == 'success' }} # Fork-local so advisory PR scans do not upload error-level SARIF and # create a second blocking check. The source pin and deltas are documented # in the called file and checked by scripts/check-governance-contract.sh. diff --git a/scripts/check-governance-contract.sh b/scripts/check-governance-contract.sh index 61ae7c5e..990cb3ee 100644 --- a/scripts/check-governance-contract.sh +++ b/scripts/check-governance-contract.sh @@ -129,8 +129,8 @@ contains "$caller" 'needs: [workflow-contract]' \ 'this contract runs before the fleet job' contains "$caller" 'needs: [workflow-contract, fleet]' \ 'the Hypatia scan runs after both the contract and the fleet report-backs' -contains "$caller" "if: \${{ always() && needs.workflow-contract.result == 'success' }}" \ - 'a dead fleet job still lets the Hypatia lane render, a failed contract does not' +contains "$caller" "if: \${{ !cancelled() && needs.workflow-contract.result == 'success' }}" \ + 'a dead fleet job still lets the Hypatia lane render; a failed contract or a cancelled run does not' # Fork delta 4: the fleet report-backs. The producer is pinned to a gitbot-fleet # commit (same owner, so the Actions policy allows the reusable); the consumer # step must sit before the grid renders, or the rows stay orange with the diff --git a/scripts/tests/test-governance-contract.sh b/scripts/tests/test-governance-contract.sh index 37d62fbf..052548aa 100644 --- a/scripts/tests/test-governance-contract.sh +++ b/scripts/tests/test-governance-contract.sh @@ -14,7 +14,7 @@ reset_fixture() { } reset_fixture bash "$fixture/scripts/check-governance-contract.sh" > "$fixture/log" 2>&1 -for mutation in allowlist-addition baseline-zero-gate baseline-operator-gate baseline-grouping-gate sarif upstream-exception effective-grid jcs-manifest jcs-canonicalizer raw-ijson k9-pin k9-require k9-indeterminate k9-verdict scanner-advisory scanner-secrets scanner-trigger scorecard-trigger stale-standards-owner scorecard-owner deed-remote scorecard-remote deed-source-sha scorecard-source-sha scorecard-no-upload scorecard-quiet-reconcile cross-owner-reusable fleet-unpinned fleet-before-contract fleet-dropped-download fleet-download-after-render fleet-download-fatal fleet-committed-evidence; do +for mutation in allowlist-addition baseline-zero-gate baseline-operator-gate baseline-grouping-gate sarif upstream-exception effective-grid jcs-manifest jcs-canonicalizer raw-ijson k9-pin k9-require k9-indeterminate k9-verdict scanner-advisory scanner-secrets scanner-trigger scorecard-trigger stale-standards-owner scorecard-owner deed-remote scorecard-remote deed-source-sha scorecard-source-sha scorecard-no-upload scorecard-quiet-reconcile cross-owner-reusable fleet-unpinned fleet-before-contract fleet-dropped-download fleet-download-after-render fleet-download-fatal fleet-committed-evidence fleet-ignores-cancel; do reset_fixture case "$mutation" in allowlist-addition) echo 'frontend/src/fork-only.ts' >> "$fixture/config/hypatia/upstream-typescript-allowlist.txt" ;; @@ -57,6 +57,7 @@ for mutation in allowlist-addition baseline-zero-gate baseline-operator-gate bas fleet-download-after-render) awk '/^ - name: Download the fleet report-backs$/{hold=1} hold && /^ - name: Render the verdict grid$/{hold=0; print; printf "%s", buf; buf=""; next} hold{buf=buf $0 "\n"; next} {print}' "$fixture/.github/workflows/hypatia-scan-reusable.yml" > "$fixture/reordered.yml" && mv "$fixture/reordered.yml" "$fixture/.github/workflows/hypatia-scan-reusable.yml" ;; fleet-download-fatal) sed -i '/^ - name: Download the fleet report-backs$/,/^ path: .gitbot-fleet\/report-backs$/{/continue-on-error: true/d}' "$fixture/.github/workflows/hypatia-scan-reusable.yml" ;; fleet-committed-evidence) sed -i '/^ - name: Clear any checked-out fleet report-backs$/,/-print -delete$/d' "$fixture/.github/workflows/hypatia-scan-reusable.yml" ;; + fleet-ignores-cancel) sed -i "s|if: \${{ !cancelled() \&\& needs.workflow-contract.result == 'success' }}|if: \${{ always() \&\& needs.workflow-contract.result == 'success' }}|" "$fixture/.github/workflows/hypatia-scan.yml" ;; # A copy is only a copy while it still comes from the audited commit and # keeps the guards whose absence froze code scanning upstream. deed-source-sha) sed -i 's@75bd5994470661a099df562d6d5ad2fe744dbd66@main@' "$fixture/.github/workflows/deed-conformance-reusable.yml" ;;