You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(fight): enact the path-filter self-win under squabble fight --apply
`squabble fight` could plan but not enact. This lands the first appliable
self-win move so the squabbler starts paying rent, not just diagnosing.
What `--apply` does (and only this, in v0.1):
- Strips an `on.*.paths` trigger filter that strands a required check. The
documented estate fix (boj-server "CI / Required Status Checks") for a
path-filtered required workflow that reports "Expected" forever and leaves a
PR `mergeable_state: blocked`.
- Classification is Missing-only: the trap manifests as a *Missing* check (the
workflow never triggered off-path). A check that ran and *Failed* did trigger,
so the filter is not its cause — that stays an escalation, not a strip.
- Writes the workflow file and stops. No commit, no push, no CI re-run
(doctrine #11, stop-first; only CI can turn a check green). The `Outcome`
stays `Red` with a new `applied` evidence section recording exactly what
changed on disk (no overclaim).
- Fail-closed + idempotent per move: an unreadable/unwritable file or a
nothing-to-strip no-op is recorded in the report, never silently skipped.
Why this is not a bypass: removing an inclusion filter makes the required check
run on *more* PRs, never fewer — a strict coverage increase. It cannot turn a
`Failed` run into `Passed` and cannot drop a required context, so `Gate::evaluate`
is untouched and the SPARK invariant (Green iff every required check ran and
passed) needs no re-proof. The `spark/` tree is unchanged.
Details:
- squabble-core: add `AppliedChange` + `Report.applied` (#[serde(default)],
back-compatible with older reports).
- squabble-fight: new `apply` module (`remove_on_paths` line-based YAML strip,
`apply_moves` fail-closed driver); classify a Missing + path-filtered required
check as the appliable `InjectPathFilterPassThrough`; dedupe self-won
path-filter traps out of the structural owner-assignment scan.
- squabble-cli: `squabble fight --apply` (propose remains the default), applied
section in the human report.
- Only `InjectPathFilterPassThrough` is appliable (pure local edit, no network).
Required-context reconcile and reusable re-pin need the GitHub ruleset API / a
target SHA and stay propose-only; the webhook leg remains honestly absent.
New tests: apply unit + temp-file end-to-end + idempotency + fail-closed;
Missing-path-filter classification; --apply flag parsing. Whole workspace green
(default + `boj` feature), fmt + clippy clean.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SUURnQUnzTXYftzWFs5pfZ
Copy file name to clipboardExpand all lines: .machine_readable/descriptiles/STATE.a2ml
+5-3Lines changed: 5 additions & 3 deletions
Original file line number
Diff line number
Diff line change
@@ -6,13 +6,13 @@
6
6
[metadata]
7
7
project = "cicd-squabbler"
8
8
version = "0.1.0"
9
-
last-updated = "2026-07-03"
9
+
last-updated = "2026-07-17"
10
10
status = "active" # active | paused | archived
11
11
12
12
[project-context]
13
13
name = "cicd-squabbler"
14
14
purpose = "CI/CD gate fighter: drives a stuck branch-protection gate to a legitimately-satisfied green by satisfying its required checks — never by admin override, enforce_admins toggle, or removing/renaming a required context. Rust fight engine + SPARK-proved gate invariant. Detachable; no hypatia/estate dependency."
{ name = "squabble-app loopback HTTP backend (axum, 127.0.0.1:7741): /health, /api/v1/diagnose, /api/v1/fight — the process the cicd-squabbler-mcp cartridge proxies to. The GitHub App *webhook* leg (check_run.requested_action + octocrab) remains honestly absent (no webhook route registered).", completion = 100 },
33
33
{ name = "boj-server expert client (feature `boj`, ureq; default build gains zero deps): `squabble fight --summon` maps each EscalateToExpert to its cartridge (Security→panic-attack-mcp, Proof→echidna-llm-mcp, Hypatia*→hypatia-mcp; dispatch-fix recorded as assessed+tracked, actuation external — no fixer cartridge exists). Fail-closed on unreachable experts; loud refusal when built without the feature.", completion = 100 },
34
34
{ name = "cicd-squabbler-mcp cartridge (boj-server-cartridges cartridges/domains/ci-cd/): squabble_fight + squabble_diagnose tools, mod.js proxy to SQUABBLE_BACKEND_URL, no ffi block (JS dispatch path)", completion = 100 },
35
+
{ name = "`squabble fight --apply` — enacts the first appliable self-win: strips an on.*.paths trigger filter that strands a required check (Missing-only classification), writing the workflow file. Fail-closed + idempotent + no commit/push/re-run; Outcome stays Red with an `applied` evidence section (no overclaim). Provably gate-strengthening (more coverage, never fewer) so the SPARK invariant is untouched. Other self-wins (context reconcile, reusable re-pin) stay propose-only pending the GitHub ruleset API.", completion = 100 },
"Implement `squabble fight --apply` (and the squabble-app GitHub App webhook leg: check_run.requested_action + octocrab) so self-win moves are enacted + re-run, not only planned.",
45
+
"Enact the remaining self-wins under --apply: required-context reconcile and reusable-workflow re-pin both need the GitHub ruleset API / a target SHA (network), so they stay propose-only until that host capability exists. (`--apply` already enacts the path-filter strip, which is a pure local file edit.)",
46
+
"Add the squabble-app GitHub App webhook leg (check_run.requested_action + octocrab — the \"Squabble!\" button) so a fight can be triggered from a PR, not only from the CLI; still honestly absent (no webhook route registered).",
45
47
"Live-fire --summon against a running boj-server with the expert backends up (hypatia :7701, panic-attack, echidna) — today's verification proves the wiring + fail-closed degradation; the experts themselves are not yet deployed.",
46
48
"Extend the SPARK invariant to cover the licence-policy gate class (v0.2; v0.1 moves are Rust-only for that class).",
47
49
"Register the CLADE (uuid 38bec2ed-bb12-44ee-9fb4-08872370c27b) into hyperpolymath/gv-clade-index (owner action).",
Copy file name to clipboardExpand all lines: README.adoc
+9-5Lines changed: 9 additions & 5 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -15,13 +15,17 @@ A standalone, per-repo CI/CD fighter that drives a stuck pipeline to a
15
15
*legitimate* green — or improves red→yellow, or fails and hands the human a
16
16
structured report. It *wins* the gate; it never cheats past it.
17
17
18
-
NOTE: Status — *v0.1 in active development (updated 2026-07-03).* The engine and
18
+
NOTE: Status — *v0.1 in active development (updated 2026-07-17).* The engine and
19
19
its formal invariant are real and verified (see below); live gate fetch via `gh`
20
20
landed in #6; `squabble fight` (classify each red as self-win | escalate |
21
21
assign-owner), the `squabble-app` loopback HTTP backend, and the boj-server
22
-
expert summoning (`--summon`, feature `boj`) landed in #13. Still open: the
23
-
`squabble apply` plumbing (moves are proposed, not yet executed) and the GitHub
24
-
App *webhook* leg (pending the owner's hosting decision). See
22
+
expert summoning (`--summon`, feature `boj`) landed in #13. `squabble fight
23
+
--apply` now *enacts* the first appliable self-win — stripping an `on.*.paths`
24
+
trigger filter that strands a required check — by writing the workflow file
25
+
(propose remains the default; `--apply` never commits, pushes, or re-runs CI).
26
+
Still open: applying the other self-wins (required-context reconcile,
27
+
reusable-workflow re-pin — these need the GitHub ruleset API / a target SHA) and
28
+
the GitHub App *webhook* leg (pending the owner's hosting decision). See
25
29
`docs/CHARTER.adoc` for the design and open owner decisions.
26
30
27
31
== Core principle: squabble ≠ bypass
@@ -42,7 +46,7 @@ check away, or any override are provably *not* paths to green.
42
46
|===
43
47
| `crates/squabble-core` | The fight engine. Pure, detachable, no hypatia/estate dependency — `diagnose → propose move → apply → verify-satisfied`. The valuable reusable part.
44
48
| `crates/squabble-fight`| The shared host brain: a2ml ecosystem-context reader + workflow ground-truth + the fight planner. One planner for the CLI and the App, so they cannot drift.
45
-
| `crates/squabble-cli` | `squabble` — runs locally or as a CI step. `fetch`, `diagnose`, and `fight` (with `--summon` under the `boj` feature to put expert verdicts on the report); apply plumbing next.
49
+
| `crates/squabble-cli` | `squabble` — runs locally or as a CI step. `fetch`, `diagnose`, and `fight` (with `--summon` under the `boj` feature to put expert verdicts on the report, and `--apply` to enact the path-filter self-win by editing the workflow — no commit/push).
46
50
| `crates/squabble-app` | The loopback HTTP backend (axum, `127.0.0.1:7741`): serves the shared planner to the boj-server `cicd-squabbler-mcp` cartridge. The GitHub App *webhook* leg (trigger button + octocrab) is still to come — no webhook route is registered.
47
51
| `spark/` | The formal heart. `Gate_Machine` + the proved theorem `green ⇔ (non-empty ∧ all passed)`.
0 commit comments