Rewrite of ChironeWp3's gate extension. The pure widget-descriptor CONSTRUCTION
is in ./gate/builders.js (L1-tested, C1); this file is the GLUE -- it depends on
the Pi runtime (pi.on, pi.registerTool, ctx.sendRaw) and is verified end-to-end at
L2 (Task D4), NOT unit-tested here. A fake-Pi runtime mock (cross-cutting
follow-up) would let it run in CI.
PRESERVED VERBATIM (load-bearing runtime glue, spec D4):
- anti-bypass tool_call hook: FORBIDDEN (nsp phase advance|reopen, decision add,
cte plan) + PROTECTED_FILES (review_decisions.jsonl, session_manifest.yaml,
cte_plan.json)
- input lock + the input hook: /nuova-domanda|/riprendi-sessione entry detection,
free-input block, the `!`-prefixed steer channel
- before_agent_start kickoff injection + the two kickoff payloads (model prose)
- agent_end prose safety net (nudges the model back to reviewer_* tools)
- session_start state reset
- exit-code contracts with the CLI (5 = gate refusal, 6 = needs human,
7 = not-ready silent no-op)
- textResult / nsp() / relayIfNspFails / advanceIfReady helpers
TWO CORRECTIVE CHANGES vs source:
1. F2 single source: workflow facts (max_phase, phase names, schema-linking phase)
come from `nsp phase meta --json`, NOT from JS-mirrored constants. The source's
PHASE_NAMES array (truncated to 7) is gone; F8/datamart can no longer drift.
2. D2/D4 widget-descriptor: reviewer interaction is emitted as a widget-descriptor
(built by ./gate/builders.js) and awaited by id via emitAndWait + the
extension_ui_response dispatcher. This replaces the source's blocking native TUI
primitives (ctx.ui.select/custom) and introduces the correlation-by-id layer
ChironeWp3 never had.
Four tools wired: reviewer_select, reviewer_decide (persists via nsp decision add),
reviewer_confirm (gate; privileged action on approve), rewrite_question. Plus the
/torna slash command for rollback. No-limbo invariant preserved: cancel/undefined
re-presents the widget; real escapes are always in the descriptor's reserved field.