fix(backend): echo Pi's RPC id so reviewer gates unblock after answer

ctx.ui.input in `pi --mode rpc` correlates extension_ui_response on its own
top-level RPC id (crypto.randomUUID), not the descriptor id the gate carries
in `title`. SessionBridge replied with the descriptor id, so Pi silently
dropped the response and the model never resumed — every reviewer widget hung
after the human answered.

SessionBridge now stores Pi's top-level m.id (pendingPiId) and replies
extension_ui_response{ id: pendingPiId, value: <uiResponse> }; value still
carries the descriptor id so the gate's internal resp.id === descriptor.id
check still holds.

The fake-pi double had masked the bug by forcing m.id == descriptor.id; it now
mirrors real Pi (distinct randomUUID, correlate on it, drop unknown ids), with
a negative regression test. SKILL.md Phase 1 also now steers multi-answer
disambiguation to reviewer_decide (multiselect).

Tests: backend 67/67, tsc clean, fake-pi contract 2/2.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-30 10:43:49 +02:00
co-authored by Claude Opus 4.8
parent 4f60b38ca2
commit 418187a4ad
8 changed files with 169 additions and 35 deletions
+15 -4
View File
@@ -100,10 +100,21 @@ Prerequisite: you must already be in Phase 1.
over real values) and `tht search find --kind evidence "<term>"`. The LSH exposes
EVERY column where a value appears — it does not collapse to a single best match,
so a value like "ablazione" may anchor on multiple columns.
2. For each ambiguity (clinical term, population, time window, outcome), present a
`reviewer_select` with the candidate interpretations (`recommended:true` on the
best) + "Altro". When a clarification is settled, move on. Pass the FULL list of
clarifications, not only the latest, when you close.
2. For each ambiguity (clinical term, population, time window, outcome), present the
candidate interpretations (`recommended:true` on the best) + "Altro". Pick the widget
by the question's shape:
- **Exactly one interpretation is correct** (mutually exclusive) → `reviewer_select`
(single-pick; it only asks — then record the choice with a `reviewer_decide`
`concept_clarified`).
- **Several answers can be simultaneously true** (e.g. more than one valid population,
procedure code, or time window) → do NOT use `reviewer_select`: single-pick buttons
force one answer and mislead the reviewer. Use `reviewer_decide` directly (it emits a
**multiselect checkbox** widget), one option per candidate, each carrying its own
`concept_clarified` decision; the reviewer checks all that apply. Keep `advance:false`
(Phase 1 still closes via the phase gate in step 3).
When a clarification is settled, move on. Pass the FULL list of clarifications, not
only the latest, when you close.
3. To close Phase 1: `reviewer_confirm kind:"phase"` (the deliberate "I'm done
clarifying" gate). Do NOT add a separate `reviewer_confirm` after each individual
clarification — those advance via `reviewer_decide` (`concept_clarified`), not via