Skip to content

Commit 620eff1

Browse files
feat(cartridges): sync bug-filing-mcp adapter/ into the bundled snapshot (#286)
<!-- SPDX-License-Identifier: CC-BY-SA-4.0 --> ## Summary Syncs the bundled `bug-filing-mcp` cartridge to match the canonical cartridge's new `adapter/` completion (companion PR in `boj-server-cartridges`), and documents the ABI=Idris2/FFI=Zig/Adapter=Zig baseline in `.claude/CLAUDE.md`. ## Changes - `cartridges/bug-filing-mcp/adapter/` (new) — verbatim sync of the unified protocol-bridge adapter (ADR-0004 internal-only, transaction-gated, REST/SSE/GraphQL/gRPC-compat). - `cartridges/bug-filing-mcp/abi/BugFilingMcp/SafeBugFiling.idr` — synced exposure-gate proof. - `cartridges/bug-filing-mcp/ffi/bug_filing_mcp_ffi.zig` — synced `pub export` fix (Zig 0.15 cross-module import requirement). - `.claude/CLAUDE.md` — the baseline documented, grounded in this repo's own cartridge conventions. ## RSR Quality Checklist ### Required - [x] Tests pass (`idris2 --typecheck` clean; `zig build test` green for ffi + adapter; `boot_smoke` still OK — 68 tools) - [x] Code is formatted - [x] Linter is clean (no new warnings or errors) - [x] No banned language patterns (no TypeScript, no npm/bun, no Go/Python) - [x] No `unsafe` blocks without `// SAFETY:` comments - [x] No banned functions (`believe_me`, `unsafeCoerce`, `Obj.magic`, `Admitted`, `sorry`) — `SafeBugFiling.idr` remains `%default total` with zero escape hatches - [x] SPDX license headers present on all new/modified source files - [x] No secrets, credentials, or `.env` files included ### As Applicable - [ ] `.machine_readable/STATE.a2ml` — no project-state change (catalogue content only) - [ ] `.machine_readable/ECOSYSTEM.a2ml` — no integration change - [ ] `TOPOLOGY.md` — no architecture change - [ ] `CHANGELOG` — deferring to the squash-merge summary - [x] New dependencies reviewed — none added - [x] ABI/FFI changes validated (`src/abi/` and `ffi/zig/` consistent) — this PR only touches the bundled cartridge snapshot, not the server's own core ABI/FFI ## Testing Verified independently, not just copied: `idris2 --typecheck` clean (0.7.0); `zig build test` green for both `ffi/` and `adapter/` (0.15.2); `deno run mcp-bridge/tests/boot_smoke.js` still `OK: serverInfo.name=boj-server, tools=68, exit=0`. ## Refs Companion PR in `boj-server-cartridges` (canonical adapter completion, extends #85). Part of #274. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_017LDhR8gtnittEWSafunnrq --- _Generated by [Claude Code](https://claude.ai/code/session_017LDhR8gtnittEWSafunnrq)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 42fec37 commit 620eff1

6 files changed

Lines changed: 481 additions & 5 deletions

File tree

.claude/CLAUDE.md

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,49 @@ Adding to this list requires explicit user approval and an unblock condition. Au
3434

3535
---
3636

37+
## ABI / FFI / Adapter Baseline (verified 2026-07-17)
38+
39+
Every cartridge's interface stack is three layers, in these languages, no
40+
exceptions:
41+
42+
| Layer | Language | Role |
43+
|---|---|---|
44+
| **ABI** | **Idris2** | Formally verified contract — dependent-type proofs, state machine or exposure-gate invariants, `%default total`, zero `believe_me`/`postulate`/`assert_total` in the trusted core. |
45+
| **FFI** | **Zig** | C-ABI implementation (ADR-0006 five-symbol pattern: `boj_cartridge_{init,deinit,name,version,invoke}`). |
46+
| **Adapter** | **Zig** | The base-level API/service surface — see below. |
47+
48+
### The "unified adapter"
49+
50+
The current canonical shape (see `cartridges/k9iser-mcp/adapter/` for the
51+
reference implementation, or `cartridges/templates/gossamer-mcp/adapter/` in
52+
`boj-server-cartridges` for the minting template): **one loopback listener**,
53+
protocol-classified (REST `/invoke`, SSE `/sse`, GraphQL `/graphql`, gRPC-compat
54+
`/grpc/<Svc>/<Method>`), that funnels every request through a **transaction
55+
gate** (`exposureSatisfied`, mirroring the cartridge's own Idris2 exposure
56+
contract when it has one) before a single dispatch call into the one Zig ABI
57+
(`ffi.boj_cartridge_invoke`). Invariants (from the adapter READMEs, verbatim):
58+
59+
- **Stateless** — all state lives behind the FFI, never in the adapter.
60+
- **Response passthrough** — whatever the FFI returns goes back to the wire
61+
unmodified (no embellishment, no silent recovery).
62+
- **`cartridge.json` is source of truth** for the tool catalogue; drift between
63+
adapter and manifest is a CI failure.
64+
- **Internal-only, never a public ingress.** Per ADR-0004 the only governed
65+
public surface is the `http-capability-gateway` (tier-2) in front of the
66+
unified Zig core; adapters bind loopback and sit behind it.
67+
68+
Naming lineage: the fuller 16-protocol-surface pattern is called the
69+
**Hexadeca-Connector** elsewhere in the estate (`hyperpolymath/hypatia`,
70+
`hyperpolymath/proven-servers`) — same Idris2-ABI + Zig-FFI substrate, more
71+
protocol surfaces. It descends from a now-**retired** V-lang reference
72+
(`developer-ecosystem/v-ecosystem/v_api_interfaces/v_api_interfaces.v`),
73+
replaced across the estate by Zig+Idris2(+Rust-client where relevant) per the
74+
V-lang ban (2026-04-10). Cartridge `adapter/` dirs keep a `SIDELINED-*.v.adoc`
75+
note documenting what was replaced — never resurrect the V version, and never
76+
substitute anything but Zig for a new adapter.
77+
78+
---
79+
3780
## PR Workflow
3881

3982
This repo squash-merges PRs. Two consequences worth knowing before pushing follow-ups:

cartridges/bug-filing-mcp/abi/BugFilingMcp/SafeBugFiling.idr

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,3 +156,47 @@ export
156156
bf_can_transition : Int -> Int -> Int
157157
bf_can_transition from to =
158158
if canTransition (intToFilingState from) (intToFilingState to) then 1 else 0
159+
160+
-- ═══════════════════════════════════════════════════════════════════════════
161+
-- Exposure / transaction-gating contract (BoJ interface-safety policy)
162+
-- ═══════════════════════════════════════════════════════════════════════════
163+
-- A port boundary must never be a gatekeeperless gateway: every adapter
164+
-- exposes the unified ABI ONLY behind this transaction gate. Mirrors
165+
-- BojRest.TrustPolicy and K9iserMcp.SafeK9iser's identically-named contract:
166+
-- caller exposure derived from the cartridge's auth.method, loopback callers
167+
-- locally trusted, X-Trust-Level enforced otherwise.
168+
169+
||| Caller trust the gateway/sidecar has established.
170+
public export
171+
data Exposure = Public | Authenticated | Internal
172+
173+
||| Required exposure inferred from cartridge auth.method.
174+
||| "none"/absent -> Public; any credential-bearing method -> Authenticated.
175+
||| bug-filing-mcp's cartridge.json declares auth.method = "none", so this
176+
||| cartridge's required exposure is Public.
177+
public export
178+
requiredExposure : (authMethodIsNone : Bool) -> Exposure
179+
requiredExposure True = Public
180+
requiredExposure False = Authenticated
181+
182+
||| The transaction gate. Loopback callers are locally trusted (mcp-bridge,
183+
||| local curl). Otherwise the presented X-Trust-Level must meet the
184+
||| required exposure. This is the total relation the Zig transaction layer
185+
||| mirrors; no dispatch may occur unless it returns True.
186+
public export
187+
exposureSatisfied : (required : Exposure) -> (presented : Exposure) -> (isLocal : Bool) -> Bool
188+
exposureSatisfied _ _ True = True
189+
exposureSatisfied Public _ _ = True
190+
exposureSatisfied Authenticated Authenticated _ = True
191+
exposureSatisfied Authenticated Internal _ = True
192+
exposureSatisfied Internal Internal _ = True
193+
exposureSatisfied _ _ _ = False
194+
195+
||| FFI: 1 if dispatch is permitted, 0 if the gate rejects.
196+
||| req/pres encoding: 0=Public 1=Authenticated 2=Internal; isLocal: 1/0.
197+
export
198+
bf_exposure_satisfied : Int -> Int -> Int -> Int
199+
bf_exposure_satisfied req pres isLocal =
200+
let r = case req of { 0 => Public; 1 => Authenticated; _ => Internal }
201+
p = case pres of { 0 => Public; 1 => Authenticated; _ => Internal }
202+
in if exposureSatisfied r p (isLocal /= 0) then 1 else 0
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// SPDX-License-Identifier: CC-BY-SA-4.0
2+
// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
3+
= bug-filing-mcp / adapter — protocol bridge
4+
:orientation: shallow
5+
6+
== Purpose
7+
8+
Exposes this cartridge's FFI over the wire protocols declared in
9+
`cartridge.json`. Dispatches tool invocations to the C-ABI exports at
10+
`../ffi/` and formats responses per protocol.
11+
12+
INTERNAL-ONLY unified adapter — not a public ingress. Per ADR-0004 the only
13+
governed public surface is the `http-capability-gateway` (tier-2); this
14+
adapter binds loopback (`127.0.0.1:9391`) and sits behind it.
15+
16+
== Files
17+
18+
[cols="1,4"]
19+
|===
20+
| File | Role
21+
22+
| `build.zig` | Zig build graph — depends on the sibling `ffi` target.
23+
| `bug_filing_mcp_adapter.zig` | Protocol dispatch. Tool catalogue matches `cartridge.json` (`research_feedback`, `synthesize_feedback`, `submit_feedback`).
24+
|===
25+
26+
== Invariants
27+
28+
* **Stateless** — all state lives behind the FFI, never in the adapter.
29+
* **Response passthrough** — whatever the FFI returns goes back to the wire
30+
unmodified (no embellishment, no silent recovery). Since the FFI itself is
31+
an honest delegation stub (see `../ffi/README.adoc` / `../ABI-FFI-README.md`
32+
equivalent), every protocol surface here answers with that same delegation
33+
notice — this adapter does not paper over what the FFI does not do.
34+
* **Cartridge.json is source of truth** for the tool catalogue; drift between
35+
adapter and manifest is a CI failure.
36+
* **Transaction-gated** — every request passes `exposureSatisfied` (mirroring
37+
`BugFilingMcp.SafeBugFiling.exposureSatisfied` in `../abi/`) before dispatch.
38+
`auth.method: "none"` in `cartridge.json` means `requiredExposure = Public`.
39+
40+
== Test/proof surface
41+
42+
Inline Zig tests cover protocol classification, tool extraction per protocol,
43+
dispatch-to-ABI funneling, and the exposure-gate truth table (cross-checked
44+
against the Idris2 contract by hand — same truth table, same encoding).
45+
46+
== Read-first
47+
48+
. `bug_filing_mcp_adapter.zig` — dispatch function and per-protocol routers.
49+
. `../cartridge.json` — the tool manifest the dispatcher mirrors.
50+
. `../abi/BugFilingMcp/SafeBugFiling.idr` — the Idris2 contract this adapter's
51+
gate mirrors.

0 commit comments

Comments
 (0)