Skip to content

Commit 4bd7aaa

Browse files
feat(app+boj): loopback HTTP backend, shared fight crate, expert summoning (Push #2) (#13)
## Summary Push #2: wire the army and make the squabbler **summonable**, while the core stays detachable and every hand-off stays honest. Three pieces: 1. **`squabble-fight` (new crate)** — the host-side fight brain extracted from `squabble-cli` (`git mv` preserves history): ecosystem-context reader + workflow ground-truth + the pure planner (`plan` / `plan_at_root`). The CLI and the App now serve **one** planner and cannot drift. 2. **`squabble-app`: stub → real** — a loopback-only axum server on `127.0.0.1:${SQUABBLE_PORT:-7741}`: `GET /health`, `POST /api/v1/diagnose` (Gate → Diagnosis), `POST /api/v1/fight` ({slug, gate, repo_root?} → Outcome/Report manifest). This is the process the boj-server `cicd-squabbler-mcp` cartridge proxies to (cartridge gateways are Deno-sandboxed with `--allow-net` only — no subprocess — so the squabbler must be reachable over loopback HTTP). The GitHub App *webhook* leg remains **honestly absent**: no webhook route is registered; a delivery 404s rather than faking a 200. 3. **`squabble fight --summon`** (feature `boj`, `ureq`; the default build compiles the client out and gains **zero deps**) — maps each `EscalateToExpert` to its specialist cartridge over boj-server's documented invoke contract: Security→`panic-attack-mcp/panic_attack_scan`, Proof→`echidna-llm-mcp/consult`, Hypatia*→`hypatia-mcp/hypatia_scan_repo`. **Honest actuation:** dispatch-fix is recorded as *assessed, actuation external* (no fixer cartridge exists today) — never "a PR was opened". **Fail-closed:** unreachable server/expert appends explicit evidence to every escalation; a summon can only *add* evidence, never remove/downgrade an escalation or change the outcome colour; a build without the feature refuses `--summon` loudly (exit 2). `squabble-core` change is additive-only: `Diagnosis` gains `Serialize/Deserialize`. No engine semantics changed; the SPARK-mirrored gate invariant is untouched. Companion PR: `hyperpolymath/boj-server-cartridges` — the `cicd-squabbler-mcp` cartridge (same branch name). ## Changes - `crates/squabble-fight/` (new): `context.rs`, `workflows.rs` (moved), `lib.rs` (planner + `plan_at_root` + tests) - `crates/squabble-app/`: axum loopback server (replaces exit-69 stub; refuses non-loopback bind; exits 69 on bind failure) - `crates/squabble-cli/`: thin `fight.rs` (delegates to shared planner; `--summon`), `boj.rs` (feature-gated client + routing + fail-closed summon), feature `boj = ["dep:ureq"]` - `crates/squabble-core/src/lib.rs`: serde derives on `Diagnosis` - `.machine_readable/descriptiles/STATE.a2ml`: milestones + next-actions updated (App backend + boj client + cartridge shipped; `--apply`/webhook leg + live-fire with deployed experts remain open) ## RSR Quality Checklist ### Required - [x] Tests pass (`cargo test --workspace` — 42 tests; plus boj-feature tests) - [x] Code is formatted (`cargo fmt --check` clean) - [x] Linter is clean (`cargo clippy --workspace --all-targets`, both feature configs — no warnings) - [x] No banned language patterns (Rust only; JS gateway lives in the cartridge repo per its own policy) - [x] No `unsafe` blocks without `// SAFETY:` comments (none added) - [x] No banned functions (`believe_me`, `unsafeCoerce`, `Obj.magic`, `Admitted`, `sorry`) - [x] SPDX license headers present on all new/modified source files (MPL-2.0) - [x] No secrets, credentials, or `.env` files included ### As Applicable - [x] `.machine_readable/descriptiles/STATE.a2ml` updated - [x] New dependencies reviewed for license compatibility: `axum`/`tokio` (App only — the App *is* the host server), `ureq` (feature-gated, off by default) — all MIT/Apache-2.0, MPL-compatible; **`squabble-core` gained no dependencies** (detachability preserved) ## Testing - `cargo test --workspace` green (default + `--features boj`); fmt + clippy clean in both configs. - **Live end-to-end:** ran `squabble-app`, then `curl` as the cartridge would — `/health` OK; `/api/v1/diagnose` returned the pure Diagnosis; `/api/v1/fight` over the real ipv6-only checkout returned the full manifest (and correctly **no longer flags the AGPL label that ipv6-only#45 removed** — the loop observes its own completed work). - **`--summon` degradation:** against a dead port, every escalation gained explicit `expert unreachable — escalation stands, fail-closed` evidence (nothing dropped, colour unchanged); the default build refused `--summon` loudly with exit 2. - A multi-angle code review ran on this diff; findings are being applied as a follow-up commit on this branch (typed expert verdicts instead of evidence-string splicing, fail-loud `SQUABBLE_PORT` parsing, minor simplifications). **Notes:** commits from this environment are unsigned (no signing key provisioned — owner/CI to sign). `gnatprove` runs in CI; the SPARK files are untouched. Live `--summon` value depends on the expert backends (hypatia :7701, panic-attack, echidna) actually being deployed — today's wiring proves the contract + graceful degradation. ## Screenshots n/a (terminal transcripts above). --- _Generated by [Claude Code](https://claude.ai/code/session_01SUURnQUnzTXYftzWFs5pfZ)_
2 parents a9bbf20 + 2371ee3 commit 4bd7aaa

14 files changed

Lines changed: 1768 additions & 364 deletions

File tree

.machine_readable/descriptiles/STATE.a2ml

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ status = "active" # active | paused | archived
1212
[project-context]
1313
name = "cicd-squabbler"
1414
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."
15-
completion-percentage = 88
15+
completion-percentage = 92
1616

1717
[position]
1818
phase = "implementation" # design | implementation | testing | maintenance | archived
@@ -28,8 +28,10 @@ milestones = [
2828
{ name = "Call-in-the-big-guns — Moves 8-9 (EscalateToExpert → hypatia/fleet/echidna/security groups; AssignGateOwner → owned-upstream / misconfigured-gate / should-be-added-to-ci). Both non-satisfying by construction; SPARK invariant untouched.", completion = 100 },
2929
{ name = "Ecosystem-context reader (host-only a2ml/k9 → RepoContext; fail-safe, never a core dependency)", completion = 100 },
3030
{ name = "`squabble fight` orchestration — classifies each red as self-win | escalate | assign-owner via workflow ground-truth; first binary to emit an Outcome/Report evidence manifest. Proven on ipv6-only#43 (5 reds classified).", completion = 100 },
31-
{ name = "squabble-app GitHub App webhook server (currently an honest stub — exits EX_UNAVAILABLE rather than fake OK)", completion = 20 },
32-
{ name = "boj-server client + cicd-squabbler-mcp cartridge + specialist expert groups (escalations become live hypatia/fleet/echidna calls) — designed, deferred to next push; EscalateToExpert is the seam", completion = 0 },
31+
{ name = "Shared fight planner extracted to squabble-fight crate (context reader + workflow ground-truth + planner) so the CLI and the App serve one brain and cannot drift", completion = 100 },
32+
{ 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+
{ 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+
{ 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 },
3335
{ name = "RSR scaffolding customisation (identity, root-allow, A2ML manifests)", completion = 80 },
3436
]
3537

@@ -39,8 +41,8 @@ milestones = [
3941

4042
[critical-next-actions]
4143
actions = [
42-
"Wire the escalation seam to boj-server: a feature-gated MCP/REST client + a cicd-squabbler-mcp cartridge, so EscalateToExpert calls hypatia-mcp/fleet-mcp/echidna live (specialist groups: Hypatia, Hypatia+Fleet, Proof, Security). Fail-closed if unreachable.",
43-
"Implement `squabble fight --apply` (and the squabble-app webhook) so self-win moves are enacted + re-run, not only planned.",
44+
"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+
"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.",
4446
"Extend the SPARK invariant to cover the licence-policy gate class (v0.2; v0.1 moves are Rust-only for that class).",
4547
"Register the CLADE (uuid 38bec2ed-bb12-44ee-9fb4-08872370c27b) into hyperpolymath/gv-clade-index (owner action).",
4648
]

0 commit comments

Comments
 (0)