Skip to content

ci: restrict abi-drift + zig-test cartridge loops to PR-changed cartridges#147

Merged
hyperpolymath merged 3 commits into
mainfrom
claude/ci-workflow-changed-files-only
May 24, 2026
Merged

ci: restrict abi-drift + zig-test cartridge loops to PR-changed cartridges#147
hyperpolymath merged 3 commits into
mainfrom
claude/ci-workflow-changed-files-only

Conversation

@hyperpolymath
Copy link
Copy Markdown
Owner

Summary

Both abi-drift (verify) and zig-test (Zig FFI Tests) currently sweep every cartridge under cartridges/* and fail the PR check if any one of them fails. With ~66 cartridges in the abi-drift allowlist and ~73 in zig-test, a PR touching a single cartridge gets gated by unrelated breakage in others — e.g. browser-mcp and orchestrator-lsp-mcp Zig failures, container-mcp/git-mcp/queues-mcp/vordr-mcp ABI drift — that has been sitting on main for weeks.

This PR constrains both per-cartridge loops on pull_request events to the intersection of:

  1. the existing allowlist / discovery, and
  2. cartridges with any file changed under cartridges/<name>/** in this PR's diff against origin/<base_ref>.

On push: main the full sweep is preserved, so cross-cutting drift is still caught at trunk. This is purely a PR-time false-positive fix, not a relaxation of the gate.

Why now

Surfaced by the investigation in PR #146: that PR touches only local-coord-mcp, but was blocked by failures in 6+ cartridges it didn't change. The other gating workflows that scan all cartridges have the same pathology; this PR fixes the two that have clean per-cartridge loops.

Changes

  • .github/workflows/abi-drift.yml — new "Restrict to cartridges changed in this PR" step; the verify loop reads the filtered set on PR, full allowlist on push. fetch-depth: 0 added so the diff can resolve.
  • .github/workflows/zig-test.yml — new "Determine cartridges to test" step; both the FFI tests loop and the shared-library build loop read the in-scope set. Catalogue/readiness steps still run on every invocation — they test cross-cartridge plumbing.

Empty changed-set on a PR (which the paths: filter shouldn't allow, but defensive) emits a ::notice:: and exits 0 instead of going red on nothing.

Out of scope

  • tests/aspect_tests.sh (the "Aspect — Thread Safety + ABI Contract + SPDX" check) asserts global invariants across the whole tree, so a "changed-files" filter doesn't fit it cleanly. Best handled with a baseline-aware ratchet — separate PR.

Test plan

  • python3 -c "import yaml; yaml.safe_load(...)" both YAML files (clean)
  • This PR itself triggers zig-test (touches .github/workflows/zig-test.yml but no cartridges/**) — expect the "Determine cartridges to test" step to print an empty scope and the per-cartridge steps to no-op via the ::notice:: path
  • PR Claude/coord fed phase1 identity #146 (touches cartridges/local-coord-mcp/**) should, when rebased onto this once merged, run abi-drift and zig-test against only local-coord-mcp and skip the unrelated failing cartridges

🤖 Generated with Claude Code


Generated by Claude Code

claude added 2 commits May 24, 2026 20:00
Both workflows currently sweep every cartridge under cartridges/* and
fail the PR check if any single one fails. With ~66 cartridges in the
abi-drift allowlist and ~73 in zig-test, a PR touching one cartridge
gets gated by unrelated breakage in others (e.g. browser-mcp and
orchestrator-lsp-mcp Zig failures, container-mcp/git-mcp/queues-mcp/
vordr-mcp ABI drift) that has been sitting on main for weeks. This
makes every cartridge-touching PR look "blocked" when it isn't.

Constrain the per-cartridge loops to the intersection of:
  (1) the existing allowlist / discovery, and
  (2) cartridges with any file changed under cartridges/<name>/** in
      this PR's diff (origin/<base>...HEAD).

On `push: main` events the full sweep is preserved, so cross-cutting
drift is still caught at trunk — this is purely a PR-time false-
positive fix, not a relaxation of the gate.

Both workflows now also `actions/checkout` with `fetch-depth: 0` so the
`git diff origin/<base>...HEAD` can resolve. An empty changed-set on a
PR (which the `paths:` filter shouldn't allow anyway, but be defensive)
emits a `::notice::` and exits 0 instead of going red on nothing.

abi-drift: new "Restrict to cartridges changed in this PR" step
  filters steps.discover.outputs.carts; the verify loop picks either
  steps.filter.outputs.carts (PR) or steps.discover.outputs.carts (push).

zig-test: new "Determine cartridges to test" step writes the in-scope
  set once; the FFI-tests and shared-library-build loops both read it.

Catalogue tests (cd ffi/zig && zig build test) and the readiness step
still run on every invocation — those test the cross-cartridge plumbing
and must always go.

This is a follow-up to PR #146's CI investigation; unblocks future
single-cartridge PRs from inheriting unrelated breakage. Aspect tests
(tests/aspect_tests.sh) use a different shape — they assert global
invariants across the whole tree — so a "changed-files" filter doesn't
fit them cleanly. Leaving aspect alone in this PR; can revisit with a
baseline-aware ratchet if needed.
So this PR's own workflow change actually runs the workflow.
abi-drift.yml already includes itself in `paths:` — apply the same
convention to zig-test.yml.
@github-actions
Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 244 issues detected

Severity Count
🔴 Critical 18
🟠 High 186
🟡 Medium 40

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Stale AI session file -- delete",
    "type": "stale",
    "file": "GEMINI.md",
    "action": "delete",
    "rule_module": "root_hygiene",
    "severity": "medium"
  },
  {
    "reason": "Issue in quality.yml",
    "type": "missing_workflow",
    "file": "quality.yml",
    "action": "create",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Issue in security-policy.yml",
    "type": "missing_workflow",
    "file": "security-policy.yml",
    "action": "create",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Action hyperpolymath/standards/.github/workflows/governance-reusable.yml@main needs attention",
    "type": "unpinned_action",
    "file": "governance.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/sanctify-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/academic-workflow-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/fireflag-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/ephapax-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/bofig-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/hesiod-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

…ll sweep

The previous refactor used:
  CARTS: ${{ github.event_name == 'pull_request' && steps.filter.outputs.carts || steps.discover.outputs.carts }}

GitHub Actions expressions short-circuit JavaScript-style: when
steps.filter.outputs.carts is an EMPTY STRING (which is exactly the
intended PR-with-no-cartridge-changes case), the expression treats it
as falsy and falls through to steps.discover.outputs.carts — the
full 66-cartridge allowlist. That's why PR #147 (touches only
.github/workflows/*) still reported drift in container-mcp / git-mcp /
queues-mcp / vordr-mcp: the filter worked correctly but its empty
output was discarded.

Replace the two-step filter+ternary pattern with one always-correct
step (id: scope) that handles both PR and push internally and writes
a single output. Mirrors what zig-test.yml already does.
@github-actions
Copy link
Copy Markdown

🔍 Hypatia Security Scan

Findings: 244 issues detected

Severity Count
🔴 Critical 18
🟠 High 186
🟡 Medium 40

⚠️ Action Required: Critical security issues found!

View findings
[
  {
    "reason": "Stale AI session file -- delete",
    "type": "stale",
    "file": "GEMINI.md",
    "action": "delete",
    "rule_module": "root_hygiene",
    "severity": "medium"
  },
  {
    "reason": "Issue in quality.yml",
    "type": "missing_workflow",
    "file": "quality.yml",
    "action": "create",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "Issue in security-policy.yml",
    "type": "missing_workflow",
    "file": "security-policy.yml",
    "action": "create",
    "rule_module": "workflow_audit",
    "severity": "medium"
  },
  {
    "reason": "Action hyperpolymath/standards/.github/workflows/governance-reusable.yml@main needs attention",
    "type": "unpinned_action",
    "file": "governance.yml",
    "action": "pin_sha",
    "rule_module": "workflow_audit",
    "severity": "high"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/sanctify-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/academic-workflow-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/fireflag-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/ephapax-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/bofig-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  },
  {
    "reason": "TypeScript file detected -- banned language",
    "type": "banned_language_file",
    "file": "/home/runner/work/boj-server/boj-server/cartridges/hesiod-mcp/adapter/mod.ts",
    "action": "flag",
    "rule_module": "cicd_rules",
    "severity": "critical"
  }
]

Powered by Hypatia Neurosymbolic CI/CD Intelligence

@hyperpolymath hyperpolymath merged commit d3da963 into main May 24, 2026
21 checks passed
@hyperpolymath hyperpolymath deleted the claude/ci-workflow-changed-files-only branch May 24, 2026 20:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants