feat(harness): single-select answers auto-confirm (reviewer_select persists)

F — reviewer_select options may now carry a `decision` payload {type, subject,
detail?, rationale?} plus an optional `advance`. Picking such an option IS the
confirmation: the gate persists it directly (tht decision add) and optionally
advances, with no redundant reviewer_decide/reviewer_confirm follow-up gate.
Options without a payload stay ask-only; back/exit/Other never persist.

Pure logic extracted + exported for unit tests: resolveSelectOutcome (classifies
the response) and decisionAddArgs (shared with reviewer_decide, DRY). Gate JS
suite 33/33 (gate_select_decision.test.js, +5); harness pytest 269 unchanged.

Contract docs updated together: reviewer_select tool description, SKILL.md
(widget summary, disciplines 2-3, Phase-1 single-pick), and the CLAUDE.md gate
note. Live verification (model truly emits reviewer_select+decision, decision in
review_decisions.jsonl, no follow-up gate) deferred to workstream G — it is
model-behavior-dependent.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-06-30 18:01:50 +02:00
co-authored by Claude Opus 4.8
parent b056ff334f
commit cef9ae4368
5 changed files with 159 additions and 52 deletions
+21 -17
View File
@@ -11,10 +11,11 @@ One question to the reviewer at a time; wait for their answer before proceeding;
NEVER advance a phase or record a decision without explicit reviewer confirmation.
The reviewer answers via the gate's **widgets** (built by `tht-gate.js`):
`reviewer_select` (single pick, no decision recorded), `reviewer_decide`
(multiselect, each selected option IS a decision — the choice is the confirmation),
`reviewer_confirm` (gate on an artifact / phase transition). Free text arrives via
the "Altro/Other" option or by prefixing `!` in chat.
`reviewer_select` (single pick; a chosen option carrying a `decision` payload IS the
confirmation and is persisted directly — an option without a payload only asks),
`reviewer_decide` (multiselect, each selected option IS a decision — the choice is the
confirmation), `reviewer_confirm` (gate on an artifact / phase transition). Free text
arrives via the "Altro/Other" option or by prefixing `!` in chat.
**Language contract (from the workspace `language` field):** the table/column
descriptions and the evidence you read are written in the workspace language (e.g.
@@ -28,16 +29,19 @@ about a domain term, ask the reviewer.
command (you invoke `tht ...` via the shell tool). NEVER run `tht phase advance`
or `tht decision add` from the shell — they are blocked by the gate's anti-bypass
hook; the gate extension records every decision via the `reviewer_*` tools.
2. **The choice is the confirmation.** For `reviewer_decide`, do NOT add a separate
`reviewer_confirm` after — each selected option already records its decision and
(if `advance:true`) advances. Add a `reviewer_confirm kind:"phase"` ONLY where the
2. **The choice is the confirmation.** For `reviewer_decide`, and for a
`reviewer_select` whose chosen option carries a `decision`, do NOT add a separate
confirmation gate after — the choice already records its decision and (if
`advance:true`) advances. Add a `reviewer_confirm kind:"phase"` ONLY where the
phase genuinely needs a deliberate gate (F1 close, F5 close, last CTE
`kind:"cte_result"`, final SQL `kind:"sql"`, F8 close) — never as a redundant
echo of a `reviewer_decide`.
3. **`reviewer_select` is for iteration only.** Use it when you propose options and
want the reviewer to pick / refine before any decision is recorded (e.g. iterating
a clarification before confirming). It records NO decision. Never use it for a
substantive decision — that's `reviewer_decide`.
echo of a recorded choice.
3. **Single pick vs multi-answer.** For a single-pick clarification or decision, use
`reviewer_select` and attach a `decision` payload (`{type, subject, detail?,
rationale?}`) to each concrete option: picking it persists that decision directly —
no follow-up `reviewer_decide`/`reviewer_confirm`. Options WITHOUT a payload only
ask (use for pure iteration before you commit). For genuinely multi-answer
decisions (several options simultaneously true) use `reviewer_decide` (multiselect).
4. **"Accept the proposal" is always an option.** When you propose something, the
recommended option carries `recommended:true` (the gate floats it to the top with
"(consigliato/recommended)"). "Altro/Other — specify…" is ALWAYS offered by the
@@ -104,8 +108,8 @@ Prerequisite: you must already be in Phase 1.
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`).
with a `concept_clarified` `decision` on each concrete option: the reviewer's pick
IS the confirmation and is recorded directly (no follow-up `reviewer_decide`).
- **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
@@ -116,9 +120,9 @@ Prerequisite: you must already be in Phase 1.
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
phase gates.
clarifying" gate). Do NOT add a separate confirmation after each individual
clarification — each is already recorded by its `reviewer_select`/`reviewer_decide`
(`concept_clarified`) choice, not via phase gates.
4. After the phase advance, update the question with the gate's `rewrite_question`
tool (it calls `tht session set-question`, which writes `question.md`
deterministically — never edit `question.md` by hand).