Commit 4bd7aaa
authored
## 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)_
14 files changed
Lines changed: 1768 additions & 364 deletions
File tree
- .machine_readable/descriptiles
- crates
- squabble-app
- src
- squabble-cli
- src
- squabble-core/src
- squabble-fight
- src
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
12 | 12 | | |
13 | 13 | | |
14 | 14 | | |
15 | | - | |
| 15 | + | |
16 | 16 | | |
17 | 17 | | |
18 | 18 | | |
| |||
28 | 28 | | |
29 | 29 | | |
30 | 30 | | |
31 | | - | |
32 | | - | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
33 | 35 | | |
34 | 36 | | |
35 | 37 | | |
| |||
39 | 41 | | |
40 | 42 | | |
41 | 43 | | |
42 | | - | |
43 | | - | |
| 44 | + | |
| 45 | + | |
44 | 46 | | |
45 | 47 | | |
46 | 48 | | |
| |||
0 commit comments