feat(feedback): implement feedback-o-tron invoke dispatch (FFI-backed)#193
Merged
Conversation
…cked) Replaces the "Grade D Alpha" stub in feedback_ffi.zig's boj_cartridge_invoke with a real implementation wired to the existing fb_* channel state machine, and switches the e2e cycle to the cartridge's actual declared tools. - boj_cartridge_invoke now backs the four cartridge.json tools with live state: feedback_register_channel -> fb_register (returns the slot), feedback_start_collecting -> fb_start_collecting, feedback_submit -> fb_submit (returns recorded + running count), feedback_get_stats -> fb_count/positive/negative/neutral + fb_state. Arguments (channel, slot, sentiment) are read from the JSON args via a small std.json-free byte scanner (jsonInt) — sufficient for the flat arg objects and version-stable. - Updated the in-file Zig unit test to drive the real register -> start -> submit x2 -> get_stats cycle and assert the real result shapes. - tests/e2e_full.sh Step 6: the previous cycle invoked non-existent tools (open_channel/summary/export/status/list_channels) the FFI never declared. Rewritten to exercise the real manifest tools via the "arguments" object (the field BojRest forwards to the FFI), asserting slot / collecting / recorded / total_feedback. Removes the FEEDBACK_OTRON skip guard. No Elixir change: the router already forwards "arguments" and returns the FFI's parsed JSON as the response body. Verification: e2e_full.sh passes bash -n + shellcheck here. The Zig could NOT be compiled locally (this sandbox's network is GitHub-only; ziglang.org returns 403 and Zig is not on GitHub releases), so the FFI is verified by CI's `zig build` (abi-drift.yml + e2e.yml) and the added Zig unit test. I'll watch the PR and fix any compile error. https://claude.ai/code/session_019tMcRS1Dm1nWjjYP4WvbJa
🏁 path-claims benchCommit NumbersHost-dependent — compare deltas across commits, not absolute values. |
🔍 Hypatia Security ScanFindings: 279 issues detected
View findings[
{
"reason": "Stale AI session file -- delete",
"type": "stale",
"file": "GEMINI.md",
"action": "delete",
"rule_module": "root_hygiene",
"severity": "medium"
},
{
"reason": "Action if: always()\n uses: actions/upload-artifact@ea165 needs attention",
"type": "unpinned_action",
"file": "e2e.yml",
"action": "pin_sha",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Action perpolymath/standards/.github/workflows/governance-reusable.yml@main\n needs attention",
"type": "unpinned_action",
"file": "governance.yml",
"action": "pin_sha",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in abi-drift.yml",
"type": "missing_timeout_minutes",
"file": "abi-drift.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in codeql.yml",
"type": "missing_timeout_minutes",
"file": "codeql.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in container-publish.yml",
"type": "missing_timeout_minutes",
"file": "container-publish.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in dogfood-gate.yml",
"type": "missing_timeout_minutes",
"file": "dogfood-gate.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in dogfood-gate.yml",
"type": "missing_timeout_minutes",
"file": "dogfood-gate.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in dogfood-gate.yml",
"type": "missing_timeout_minutes",
"file": "dogfood-gate.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
},
{
"reason": "Issue in dogfood-gate.yml",
"type": "missing_timeout_minutes",
"file": "dogfood-gate.yml",
"action": "flag",
"rule_module": "workflow_audit",
"severity": "medium"
}
]Powered by Hypatia Neurosymbolic CI/CD Intelligence |
hyperpolymath
marked this pull request as ready for review
June 4, 2026 12:40
hyperpolymath
added a commit
that referenced
this pull request
Jun 5, 2026
…e2e (#194) ## Why #193 merged with E2E red — feedback invokes returned `{}`. I installed Zig 0.15.2 (it's on **PyPI** — the sandbox blocks `ziglang.org` but not PyPI) and traced it to root cause, locally, end-to-end. **The `{}` was never my FFI code** (it compiles, 15/15 unit tests pass, and the real `boj-invoke` CLI returns correct JSON). It was two infra faults in one e2e step, plus an architectural fact: 1. **`boj-invoke` was never built** — the "Build FFI libraries" step ran `zig build`, but the CLI the Elixir Invoker shells out to is only produced by `zig build invoke`. No CLI → `:cli_missing` → `{}` for *every* FFI tool invoke. 2. **Cartridge `.so`s were never built either** — after `cd ffi/zig`, the `for cart in cartridges/*/ffi` loop globbed from the wrong directory, matched nothing, and `|| true` hid it. 3. **The invoker is fork-per-request** (ADR-0005): each invoke is a fresh process, so the cartridge's in-memory channel state does **not** persist between HTTP calls. A multi-step `register → submit → stats` cycle cannot span calls (proved: two separate submits both `recorded:false`). ## What - **`feedback_ffi.zig`** — `feedback_submit` is now **self-provisioning**: when the slot isn't an active collecting channel (the stateless case) it registers + starts collecting before recording, so a lone submit returns `recorded:true` in one call. Within one process (a pooled invoker, or the unit test) an already-collecting slot is reused and counts still accumulate. Added a cold-start unit test → **15/15 pass**. - **`tests/e2e_full.sh`** Step 6 — assert only what a stateless invoker can truthfully deliver: register returns a slot, each submit records, `get_stats` returns the stats shape. Cross-call accumulation (the full state machine) is left to the pooled-invoker follow-up and is covered by the in-process Zig unit test. - **`.github/workflows/e2e.yml`** — build `boj-invoke`; contain the `cd` in a subshell so the cartridge loop globs from the repo root; tidy the loop to an explicit `if` (silences SC2015). ## Verification (local, Zig 0.15.2 — CI's exact version) ``` zig build # exit 0, libfeedback_mcp.so zig test feedback_ffi # All 15 tests passed # real boj-invoke CLI (what the Invoker calls): register → {"slot":0,"channel":2,"state":1} submit → {"recorded":true,"slot":0,"sentiment":1,"feedback_count":1} stats → {"slot":0,"total_feedback":0,...} # truthfully per-call ``` The full Elixir server e2e needs `hex.pm` (blocked in this sandbox), but `boj-invoke` is exactly what the Invoker shells out to, so the feedback invoke path is fully covered. ABI is unchanged (dispatch body only) → `abi-drift` stays green. 🤖 Draft via Claude Code. --- _Generated by [Claude Code](https://claude.ai/code/session_019tMcRS1Dm1nWjjYP4WvbJa)_ Co-authored-by: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Builds feedback-o-tron for real (the follow-up you green-lit), replacing the "Grade D Alpha" stub in
feedback_ffi.zig'sboj_cartridge_invokewith a live, state-machine-backed implementation, and switching the e2e cycle to the cartridge's actual declared tools.Key insight
The stub already dispatched the correct
cartridge.jsontool names (feedback_register_channel/start_collecting/submit/get_stats) — it just returned{"status":"stub"}. The old e2e cycle invoked tools that never existed (open_channel/summary/export/status/list_channels), which is why it got{}. So the fix is: make the FFI real, and point the test at the real tools.Changes
feedback_ffi.zig—boj_cartridge_invokenow backs the 4 manifest tools with the existingfb_*channel state machine:feedback_register_channel→fb_register→{"slot":N,"channel":C,"state":1}feedback_start_collecting→fb_start_collecting→{"slot":N,"collecting":true,"state":2}feedback_submit→fb_submit→{"recorded":true,"slot":N,"feedback_count":K}feedback_get_stats→fb_count/fb_positive_count/… →{"total_feedback":…,"positive":…,…}channel/slot/sentimentare read from the JSON args via a small,std.json-free byte scanner (jsonInt) — sufficient for the flat arg objects and version-stable.feedback_ffi.zigtest — the in-file invoke test now drives the realregister → start → submit ×2 → get_statscycle and asserts the real shapes (sozig build testcovers it in CI).tests/e2e_full.shStep 6 — rewritten to exercise the real tools via the"arguments"object (the field BojRest forwards to the FFI); assertsslot/collecting:true/recorded:true/total_feedback. Removes theFEEDBACK_OTRONskip from fix(rest): restore tiered /menu + make /cartridges & feedback-o-tron E2E truthful #192.argumentsand returns the FFI's parsed JSON as the body. The cross-language flow (Zig JSON → Invoker parse → Jason re-encode → test) lands the keys the assertions read.Verification — please note
e2e_full.shpassesbash -n+shellchecklocally. The Zig could not be compiled locally: this sandbox's network is GitHub-only —ziglang.orgreturns 403 and Zig isn't on GitHub releases, and apt is blocked. So the FFI is verified by CI (abi-drift.yml+e2e.ymlboth runzig build) and the added Zig unit test. I'll watch this PR and fix any compile error promptly. (If you'd rather I verify locally, allowingziglang.orgin the environment's network policy would let me install Zig 0.15.1.)The ABI is unchanged (only the dispatch body), so
abi-driftshould stay green.🤖 Draft via Claude Code.
Generated by Claude Code