ci: make path-filtered required gates always report (unblock stuck PRs)#216
Merged
Merged
Conversation
Seven gates were workflow-level path-filtered (on.*.paths). When a PR touched none of a gate's paths the workflow never ran, so its REQUIRED status check stayed "Expected" forever and the PR sat at mergeable_state=blocked — even with every other check green and the PR approved (e.g. #213 flake.lock bump, #215 scripts-only). The asymmetry this fixes: a path-filtered workflow that never runs BLOCKS, whereas a job skipped via `if:` reports SUCCESS to required checks. For each of abi-drift, backend-assurance, e2e, lsp-dap-bsp, proofs, truthfulness, zig-test: * drop the workflow-level `on.*.paths` filter (always run, so the required check is always created), * add a lightweight `changes` job that recomputes the gate's original path set via `git diff origin/<base>...HEAD` (the same pattern abi-drift/zig-test already used internally for per-cartridge scoping), * gate every heavy job with `needs: changes` + `if: needs.changes.outputs.run == 'true'`. When nothing relevant changed the heavy job is skipped (a passing required check), so unrelated PRs are no longer blocked AND don't pay for the heavy work. Fail-safe: the detector defaults to run=true and only skips when a SUCCESSFUL diff shows no relevant path changed (any fetch/diff error or unknown base => run). The dangerous direction — skipping a gate that should run — cannot happen on detection failure. Each regex mirrors the gate's original `paths:`, so a relevant change runs it exactly as before. No branch-protection change needed: job/check names are unchanged. workflow_dispatch added to all 7 for manual full runs. Validated locally: actionlint clean on all 7 (only pre-existing shellcheck infos remain, untouched); detection regexes unit-tested against representative change-sets (77/77, incl. #213/#215 -> skip and abi/ffi/src changes -> run). Note: editing a workflow file no longer self-triggers its heavy gate (kept out of each regex), so workflow-only PRs like THIS one exercise the skip path and stay green. https://claude.ai/code/session_019tMcRS1Dm1nWjjYP4WvbJa
🔍 Hypatia Security ScanFindings: 272 issues detected
View findings[
{
"reason": "Stale AI session file -- delete",
"type": "stale",
"file": "GEMINI.md",
"action": "delete",
"rule_module": "root_hygiene",
"severity": "medium"
},
{
"reason": "Issue in abi-drift.yml",
"type": "missing_timeout_minutes",
"file": "abi-drift.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in codeql.yml",
"type": "missing_timeout_minutes",
"file": "codeql.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in container-publish.yml",
"type": "missing_timeout_minutes",
"file": "container-publish.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in dogfood-gate.yml",
"type": "missing_timeout_minutes",
"file": "dogfood-gate.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in dogfood-gate.yml",
"type": "missing_timeout_minutes",
"file": "dogfood-gate.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in dogfood-gate.yml",
"type": "missing_timeout_minutes",
"file": "dogfood-gate.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in dogfood-gate.yml",
"type": "missing_timeout_minutes",
"file": "dogfood-gate.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in dogfood-gate.yml",
"type": "missing_timeout_minutes",
"file": "dogfood-gate.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in dogfood-gate.yml",
"type": "missing_timeout_minutes",
"file": "dogfood-gate.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
}
]Powered by Hypatia Neurosymbolic CI/CD Intelligence |
This was referenced Jun 13, 2026
Open
hyperpolymath
added a commit
that referenced
this pull request
Jun 13, 2026
…219) Documentation-only follow-up to #216 — captures the required-gate "skip-shim" as a durable convention so it can't be silently regressed, and records the work in machine-readable state. **No workflow or code changes.** ## What's added - **`docs/AI-CONVENTIONS.adoc`** — new "CI / Required Status Checks" section (the do/don't rule). Also fixes a **stale** Banned-Languages row: `TypeScript → AffineScript` (not ReScript — retired as the destination 2026-04-30, per CLAUDE.md). - **`.claude/CLAUDE.md`** — matching "CI / Required Status Checks" section for AI agents. - **`docs/wikis/CI-and-Required-Checks.adoc`** — new developer wiki page: the problem (path-filtered required check → permanent "Expected" → blocked PR), the fix, a copy-paste workflow template, a do/don't table, and the green-but-blocked diagnostic. Linked from `Home.adoc`. - **`.machine_readable/6a2/STATE.a2ml`** — 2026-06-13 session entry (#216 + merged #213/#215 + follow-ups #218/#46/#47/#48); `last-updated` bumped. - **`.machine_readable/6a2/PLAYBOOK.a2ml`** — `[ci-required-gates]` runbook section. - **`CONTRIBUTING.md`** — brief pointer note. Both a2ml files verified to still parse as TOML. Draft for your review. https://claude.ai/code/session_019tMcRS1Dm1nWjjYP4WvbJa --- _Generated by [Claude Code](https://claude.ai/code/session_019tMcRS1Dm1nWjjYP4WvbJa)_ Co-authored-by: Claude <noreply@anthropic.com>
5 tasks
hyperpolymath
added a commit
that referenced
this pull request
Jun 20, 2026
…229) ## Summary Wires `scripts/hcg-surface-drift-check.sh` (landed in boj-server#228, merged 2026-06-19) into GitHub Actions, so the surface⊆policy invariant the ADR calls its largest declared risk is re-proven on every PR rather than relying on the manual re-verification stamp in `config/gateway-policy-boj.yaml`'s header. PR #228 explicitly flagged this CI wiring as the follow-up step — "a CI wiring PR should follow [the always-trigger + changes-job] pattern. Out of scope here." This is that follow-up; the script, the router, and the policy are unchanged. ## What lands A single new file: `.github/workflows/hcg-surface-drift.yml`. The workflow follows the boj-server "always-trigger + changes-job" pattern documented in `docs/wikis/CI-and-Required-Checks.adoc` and `.claude/CLAUDE.md` §"CI / Required Status Checks": - **No `on.*.paths`** — the check is always created. A path-filtered required workflow that never fires is the failure mode that stranded #213/#215 until #216 fixed it; this gate is built to never re-introduce it, regardless of whether it later joins `required_status_checks`. - **Lightweight `changes` job** recomputes relevance via `git diff origin/<base>...HEAD` against the four paths this gate cares about — router (`elixir/lib/boj_rest/router.ex`), live policy (`config/gateway-policy-boj.yaml`), the drift script (`scripts/hcg-surface-drift-check.sh`), and the workflow file itself. Fail-safe to `run=true` on any diff failure. - **Heavy `check` job** is `needs: changes` + `if: needs.changes.outputs.run == 'true'`. A skipped `if:` reports SUCCESS to any future required-context list, so unrelated PRs never pay for it and can never be blocked by it. - **Pinned action**, **timeout-minutes**, **concurrency group**, **`permissions: contents: read`**, **SPDX header** — matches the canonical pattern in `.github/workflows/abi-drift.yml`. The `check` job invokes the script with `bash scripts/hcg-surface-drift-check.sh -v` (matching the test plan in #228) so it works regardless of the script's file mode — #228 committed the script as 0644. ## What this PR does NOT do - **Does NOT** modify the runbook §1.5 ("Gateway-side prerequisites"). Adoption of the CI gate into the §1.5 checklist is a one-line owner-driven runbook update — the PR #228 deliberate boundary stays in place. - **Does NOT** add the new check to `.github/settings.yml`'s `required_status_checks` list (currently `hypatia-scan` + `codeql`). Promotion to required is a settings change for the owner to make once the gate has run green on a few PRs. - **Does NOT** modify the live policy, the example policy, the router, the script, or any other Phase E artefact. The change is wholly within `.github/workflows/`. - **Does NOT** pre-empt the §6.4 Trustfile flip (`tier_2_gateway.status` stays `PENDING`), the staging soak (§3.3), or cerro-torre `.ctp` signing — all of which remain owner-driven per the channel doctrine reaffirmed in #207 / #224. - Per the single-lane HCG channel discipline (pattern set in `http-capability-gateway` PRs #14, #22, #26, #30, #38 and `boj-server` PRs #168, #173, #224, #226, #228): joint-close is owner-only. **This PR refs but does not close `standards#100`.** ## Channel state note This session could not read `hyperpolymath/standards#91` / `#100` (the session's MCP repo scope is restricted to `http-capability-gateway` and `boj-server`), so the brief's instructed status comment on `standards#91` could not be posted. State was reconstructed from the canonical sources in this repo (ADR-0004, the integration plan, the audit, the rollout runbook, the live policy, `docs/wikis/CI-and-Required-Checks.adoc`) plus the merged-PR history of both in-scope repos. Analysis: Phase A/B/C/D closed; Phase E (`standards#100`) is the only open phase; #228 (2026-06-19) is the most recent advance and explicitly named this CI wiring as the next step. ## Test plan - [ ] **Required**: the `changes` job runs and emits `run=true` (because `.github/workflows/hcg-surface-drift.yml` matches the path regex), so the `check` job is gated through, not skipped, on this PR. - [ ] **Required**: the `check` job runs `bash scripts/hcg-surface-drift-check.sh -v` and exits 0 with the OK message — current `main` (64a70c5) has 7 wired routes, 28 policy rules, no drift; locally re-verified on this branch. - [ ] **Synthetic skip**: on a follow-up PR that touches none of the four watched paths, `changes.outputs.run` is `false` and `check` reports `skipped` (which counts as success for any required-context list). - [ ] **Synthetic drift**: a temporary PR adding `get "/__drift_test__"` to `elixir/lib/boj_rest/router.ex` without a matching policy rule fires `run=true`, `check` exits 1 with the route listed under `DRIFT:`, and the PR is blocked from merge if/when this gate is promoted to required. - [ ] No `actionlint` / Hypatia / SPDX gate fires on the new workflow file. Refs hyperpolymath/standards#91 Refs hyperpolymath/standards#100 🤖 Generated with [Claude Code](https://claude.com/claude-code) --- _Generated by [Claude Code](https://claude.ai/code/session_019cKmxx6AkNjzhXT6ZoxGfx)_ Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this fixes
PRs #213 (dependabot
flake.lock) and #215 (scripts-only) are stuck atmergeable_state: blockeddespite every check green (and #213 approved). Root cause: seven gates are workflow-level path-filtered (on.*.paths). When a PR touches none of a gate's paths the workflow never runs, so its required status check stays "Expected" forever → permanent block.The asymmetry this exploits: a path-filtered workflow that never runs blocks; a job skipped via
if:reports SUCCESS to required checks. This converts the former into the latter.Change (uniform across all 7)
abi-drift,backend-assurance,e2e,lsp-dap-bsp,proofs,truthfulness,zig-test:on.*.pathsfilter → the workflow always runs, so the required check is always created.changesjob that recomputes the gate's original path set viagit diff origin/<base>...HEAD(the same patternabi-drift/zig-testalready use internally).needs: changes+if: needs.changes.outputs.run == 'true'. Nothing relevant changed → heavy job skipped → passes → PR unblocked and no wasted CI.No branch-protection change needed — job/check names are unchanged.
workflow_dispatchadded to all 7 for manual full runs.Safety
run=trueand only setsfalsewhen a successful diff shows no relevant path changed (any fetch/diff error or unknown base ⇒ run). The dangerous direction — skipping a gate that should run — can't happen on detection failure.paths:, so a relevant change runs it exactly as before (verified: 77/77 unit checks —abi/→proofs+abi-drift,ffi/→zig+truthfulness+abi-drift,SafetyLemmas.idr→backend-assurance+proofs, etc.; chore(deps): bump nixpkgs from8c3cedeto8c91a71#213/feat(scripts): extend §1.5 verb-canary coverage (Phase E) #215 → all skip).Validation
Self-validating: editing a workflow file no longer self-triggers its heavy gate (kept out of each regex), so this PR's own checks exercise the skip path — the gates should report skipped/success here, demonstrating the unblock live.
Draft — why
I can't runtime-test the
run=truebranch (it needs the GH runners), and this changes how required gates fire, so it wants your eyes before merge. Mark ready / squash-merge when you're satisfied. After it lands, re-check #213/#215 — they should flip fromblockedto mergeable.https://claude.ai/code/session_019tMcRS1Dm1nWjjYP4WvbJa
Generated by Claude Code