Skip to content

Roll unified-gated-adapter + SSE + regen-trigger into the iseriser scaffold #90

Description

@hyperpolymath

Roll the pilot pattern (boj-server#73) into the iseriser scaffold so every current + future -iser ships it by construction:

  • unified transaction-gated adapter (one internal listener; REST/SSE/GraphQL/gRPC → one gated dispatch → one Zig ABI; mirrors an Idris2 exposure contract). Supersedes the ssg-era 3-parallel-port adapter estate-wide.
  • *-regen.yml trigger (mirrors boj-build.yml; already templated for k9iser into rsr#58/v3#76/repo#52 — generalise in the scaffold).
  • adds SSE + the transaction gate to all 28 -isers.
    Parent: epic. Sibling of the gateway/source-of-truth/gitignore sub-issues.

Activity

  1. hyperpolymath commented on May 20, 2026

    @hyperpolymath
    OwnerAuthor

    Status — 2026-05-20

    Scaffold-side: shipped (with gaps). iseriser PR#12 merged 2026-05-19 (109b656) — src/codegen/scaffold.rs now emits both .github/workflows/<name>-regen.yml (fire-and-forget central-trigger, mirrors k9iser-regen.yml) and adapter/<name>_adapter.zig (unified gated adapter with exposureSatisfied Zig mirror + 4 truth-table tests). cargo test green (49 unit + 9 integration).

    Estate fan-out: correctly gated on #91. PR#12 explicitly does NOT touch the 28 existing -iser repos; until the http-capability-gateway tier-2 (standards#91, ~8–12wk) is wired, the unified core stays internal/loopback and consumer K9 Dogfood gates stay red (tracked, not masked).

    Audit — gaps between scaffold templates and the k9iser pilot (boj-server/cartridges/k9iser-mcp/adapter/k9iser_adapter.zig, 293 lines). To be addressed before estate fan-out begins:

    1. Topology question. Pilot adapter lives in boj-server/cartridges/<name>-mcp/adapter/, alongside its build.zig, cartridge.json, and the <name>_ffi Zig module. Scaffold drops adapter/<name>_adapter.zig into the new -iser repo, where none of those exist (@import(\"{name}_ffi\") against nothing) — not buildable. Need a decision: doc-reference, copy-into-boj-server template, or rendezvous topology change.
    2. Dispatch is a stub. dispatchTool discards tool+body and writes a hardcoded {\"status\":\"dispatched\"} placeholder. Pilot's dispatch() calls ffi.boj_cartridge_invoke with null-terminated args, sized out-buffer, and a 0/-1/-2/-3 → 200/404/400/500 return-code mapping. Also missing: ffi.boj_cartridge_init/deinit lifecycle in main().
    3. Two of four advertised protocols unimplemented. Module header documents REST + SSE + GraphQL + gRPC. Routing only handles /sse and falls through to REST for everything else. No Protocol enum, classify(), or toolFor() covering all four. Pilot has them all plus tests.
    4. HTTP response buffer-aliasing bug. dispatchRest writes the body into resp_buf and returns a slice into it. handleConnection then bufPrint(&resp_buf, …, .{result.status, result.body}) — overwriting the buffer before reading result.body out of it. Output will be corrupted. Pilot uses a separate hdr buffer (writeHttp).
    5. Helper bugs. headerValue never trims or splits on :; won't find non-leading headers. jsonField searches for literal \"key\": \" (one space after colon); the common {\"key\":\"value\"} form won't match. Pilot's jsonStringField walks colon + whitespace properly.
    6. SSE framing differs. Pilot emits event: open → event: result|error → event: done. Scaffold emits a single data: line. Won't interop with pilot-compatible consumers.
    7. Scaffold CI doesn't compile-check the generated Zig. The 4 truth-table tests live inside the template string; iseriser's cargo test only verifies codegen output, never zig tests the rendered file. Coverage only fires if a downstream -iser builds. Also missing: presentedExposure, classify, toolFor, dispatch tests.
    8. No adapter/build.zig emitted. Pilot ships one; scaffold doesn't.
    9. License (AGPL pilot vs PMPL scaffold — likely deliberate) and tool-name default ({name}_generate vs pilot's k9_generate) noted as low-priority drift.

    Issue stays OPEN: fan-out + audit follow-ups both still owed. Sibling state — #91 gateway (the blocker) still open; #92 (Idris2 SoT) has live PR work landing today (iseriser #13/#14/#15/#16/#20/#21/#22); #93 stays parked behind #91.

    🤖 Generated with Claude Code

  2. hyperpolymath commented on May 20, 2026

    @hyperpolymath
    OwnerAuthor

    Status — 2026-05-20 (end-of-session)

    Following the audit comment above, two follow-up PRs landed today:

    iseriser#23 MERGED — surgical revert of the wrong-place adapter emission. The audit had identified that PR #12 emitted adapter/<name>_adapter.zig into the new -iser repo, but the unified transaction-gated adapter belongs to the boj-server cartridge (boj-server/cartridges/<name>-mcp/adapter/), not to the -iser repo itself. The pilot k9iser confirms: k9iser repo has no adapter; the adapter lives at boj-server/cartridges/k9iser-mcp/adapter/. PR #23 dropped the wrong-place emission (gaps 1–8 of the audit comment) while keeping <name>-regen.yml (correctly emitted in the -iser repo, since it's the trigger that fires into boj-server).

    iseriser#24 MERGED — new iseriser cartridge subcommand: full scaffolder for the boj-server cartridge skeleton (standards#89 Phase 2b). 13 files: top-level (README + cartridge.json + mod.js + panels/manifest.json), Idris2 ABI (README + <iser>-mcp.ipkg + <Iser>Mcp/Safe<Iser>.idr with the exposure-gate contract), Zig FFI (README + build.zig + <iser>_ffi.zig implementing the ADR-0006 5-symbol C ABI), and the unified gated adapter (README + build.zig + <iser>_adapter.zig routing REST + SSE + GraphQL + gRPC-compat behind the transaction gate). Modelled on the k9iser-mcp pilot. End-to-end verified against the actual boj-server/cartridges/ tree: idris2 --build on the emitted .ipkg type-checks SafeChapeliser (RC=0), zig build test on ffi/ passes 4/4, zig build test on adapter/ passes 5/5.

    iseriser#25 MERGED — small correction: scaffold now emits depends = base, contrib on the cartridge ipkg to match the pilot convention.

    iseriser#26 OPEN — docs PR (CHANGELOG entries + README subcommand row + STATE.a2ml refresh).

    Where this leaves #90

    #90 stays OPEN until the fan-out lands. Closing it before #91 would lose tracking of the cross-repo work.

    Suggested closure when #91 is wired

    The fan-out will be a 28 × <name>-regen.yml PR batch (each repo trivially adds the workflow + flips its loopback URL to the gateway endpoint) plus, where missing, a iseriser cartridge --output boj-server/cartridges/ invocation per absent cartridge. At that point #90 closes.

    🤖 Generated with Claude Code

  3. hyperpolymath commented on Aug 27, 2026

    @hyperpolymath
    OwnerAuthor

    Next step (re-verified 2026-08-27): one of three items landed — the scaffold emits a *-regen.yml trigger. SSE and the transaction gate are entirely absent: git grep -lIiE 'text/event-stream|SSE' and 'transaction.gate|gated' on iseriser@origin/main both return 0 files. The pilot to copy from is merged (boj-server#73).

    → Add the gated-dispatch adapter + SSE surface to iseriser/src/codegen/scaffold.rs, then re-emit across the 26 -isers (not 28 — see #89 correction). Medium; do it in the same pass as #331's scaffold fix, since the emitted boj-build.yml template is the broken one.

  4. hyperpolymath commented on Sep 3, 2026

    @hyperpolymath
    OwnerAuthor

    2026-09-03 — second of the three items landed; SSE is still absent, and now deliberately so

    Following the 2026-08-27 note (regen trigger landed, SSE and the transaction gate absent): iseriser PR #101 is merged (b898428d), adding the gated adapter dispatch to the emitted boj-build.yml.

    Scoreboard for this issue:

    Item State
    regen trigger (*-regen.yml) landed (2026-08-27)
    unified gated adapter landed — scheme-checked endpoint, jq -nc --arg payload, curl --fail-with-body, unset = loud skip
    SSE still absent — see below

    Why SSE was not wired, and why that is a decision rather than an omission

    The dispatch deliberately targets /cartridge/:name/invoke, not /sse. boj-server's router calls send_chunked(200) before it dispatches (router.ex:100), so an SSE request returns HTTP 200 even when the work behind it fails. That cannot back a hard-failing gate — a CI step pointed at /sse would be green by construction, which is the exact fake-gate shape this estate keeps finding.

    So SSE stays open here, and closing it needs either a status-bearing SSE contract on the server side or an out-of-band result channel. Recording it rather than quietly dropping it.

    Still true: nothing is proven end-to-end, because CI cannot reach a BoJ server at all (#91 tier-2 gateway unbuilt).

  5. added
    architectureStructural/system-level shape and runtime behaviour
    scope:estateAffects many or all repos across the estate
    status:readyFully specified and ready to be picked up
    on Sep 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    architectureStructural/system-level shape and runtime behaviourmajorMajor / load-bearing workpriority:p2Normal - queue itrequirements-targetTracked requirements-target item (joint-close)scope:estateAffects many or all repos across the estatestatus:readyFully specified and ready to be picked up

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions