Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
77efefe
feat(planning): refuse a rec change that leaves the alternatives unre…
claude Oct 11, 2026
689fcf7
fix(planning): show the whole recommendation in the Rec choice (#5815)
claude Oct 11, 2026
e84085e
fix(planning): say "Recommendation revised." once and retire it once …
claude Oct 11, 2026
083f932
feat(planning): let revise move a question to another group and add g…
claude Oct 11, 2026
2e3db79
test(planning): check the two-line clamp on a non-Rec option (#5815)
claude Oct 11, 2026
a942dd9
fix(planning): name the stale ancestor behind an upstream-pending que…
claude Oct 11, 2026
acc1430
fix(planning): lay out needs chips in one row and mark met ones in te…
claude Oct 11, 2026
6c9d30a
fix(planning): say "decided after you answered" on a stale question a…
claude Oct 11, 2026
ce65cef
fix(planning): offer Reconfirm on a kept accept only while its recomm…
claude Oct 11, 2026
0ad9cf4
fix(planning): take the user to the dependents a changed answer made …
claude Oct 11, 2026
6a4d6db
fix(planning): keep Claude's reply to an ask in the question's Thread…
claude Oct 11, 2026
debc271
fix(planning): keep the question body visible when the note or confli…
claude Oct 11, 2026
ea32c10
Merge origin/main into feat/planning-interview-page-batch
kyle-sexton Oct 13, 2026
b7fd2f2
test(planning): check #5913 against main's detail layout
kyle-sexton Oct 13, 2026
37f11b6
fix(planning): read a terminal decision's stale prerequisite as chang…
kyle-sexton Oct 13, 2026
f373954
Merge origin/main into feat/planning-interview-page-batch
kyle-sexton Oct 13, 2026
da42e16
test(planning): keep the CI user name and British spellings out of ui…
kyle-sexton Oct 13, 2026
7ae52e7
fix(planning): drop Reconfirm on an older terminal accept after a rec…
kyle-sexton Oct 13, 2026
28d8c3a
fix(planning): enable the older terminal accept time fallback (#5910)
kyle-sexton Oct 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .changes/planning/feat-planning-interview-page-batch-9e5bf7a8.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
bump: minor
---

### Added

- Interview page: a `reply` or `revise` that changes the recommendation must carry the revised alternatives or `keepAlternatives: true` (CLI `--keep-alternatives`), which states the current ones were checked against it; with neither it is refused and nothing is written. `reply` now takes `alternatives` with `rec`, so an answer and a full revision land in one op, and the history line records whether the alternatives were rewritten or kept (#5801).
- Interview page: `revise` takes `group` (CLI `--group`) to move a question to another existing group in place, keeping its id, thread and answer, with a history line and a warning when either group's summary predates the move; a new `group-order` op reorders the groups, which the Groups rail follows (#5914).

### Fixed

- Interview page: the Rec choice shows the whole recommendation the user accepts, not only its first sentence; the Accept all list keeps its one-sentence preview (#5815).
- Interview page: the revised-recommendation banner says "Recommendation revised." once (a change with no `why` now stores `revised: true`, and data holding the old fallback sentence reads the same), and shrinks to a "Revised" chip, with Previous kept, once the user has opened the question since its latest revision (#5916).
- Interview page: an upstream-pending question names the stale question(s) that cause it, not its direct prerequisites: the server adds a derived `upstreamStale` list (every stale ancestor, nearest first, ties by id; never written), the rail chip reads `Waiting on <them>`, and the banner links them with "Reconfirm … (under Review), or answer this one anyway"; the row is dimmed less and brightens on hover and focus, so it reads as waiting, not locked (#5922).
- Interview page: a question's "needs Qn" chips share one wrapped row under its title instead of one line each, and a met prerequisite carries a check mark labeled "answered" for screen readers, so met and unmet differ by more than color (#5917).
- Interview page: the stale banner says "Q5 was decided after you answered this" when the prerequisite had no decision before this answer, and "Q5 changed" only when an earlier decision was replaced; the header meter adds "· N to review" (the Review filter's count, which opens that filter) whenever an answered question needs review (#5908).
- Interview page: a stale question whose kept accept predates a change to its own recommendation offers no Reconfirm, since that row would record text the user may not have read; the banner reads "Pick an answer.", `a` arms `Rec` with the revised text, and Previous keeps the old one (#5910).
- Interview page: a save that makes other answers stale takes the user to the first of them in dependency order, switching Show to Review when the current filter hides it, names them in the toast ("Saved Q2. Q3 and Q5 need review; 2 more wait on them."), and `n` visits the rest first; a save that stales nothing navigates as before (#5909).
- Interview page: Claude's latest reply stays in the question's Thread as well as the latest-reply box, so a reply to the user's ask is never shown only above the recommendation, and a reply in the same second as the event it answers sorts after it (#5817).
- Interview page: a browser check confirms that a long note or an open conflict box leaves the question body the share of the detail pane it had, with the recommendation in view, on the layout the answer-controls change introduced (#5913).
13 changes: 7 additions & 6 deletions plugins/planning/skills/interview/context/surface.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,10 +65,11 @@ When the first wake prompts for permission, offer the user one allow rule, `Bash
| `op` | Fields | Use |
|---|---|---|
| `handle` | `seqs` | Plain accepts (no new note text), `accept-audit` with its fanned-out accepts, `reopen`, `confirm`, `confirm-understanding` with `confirm`, `undo`, `wrapup`: no reply (R9) |
| `reply` | `id`, `text`, `seq`, `kind` (`reply` by default, `rephrase`, `note`), `rec` + `why` + `affects`, `resolution`, `handled`, `force` | Answer an ask or rephrase; `rec` revises the recommendation and is refused when an alternative repeats or contains it (use `revise` with `alternatives` instead); `resolution` records the accepted reading of the question's counted `own` answer (refused when none counts), which every export gives as the answer while the user's words become its note |
| `revise` | `id`, `title`, `short`, `facts`, `basis`, `rec`, `why`, `text`, `alternatives`, `commits`, `dependsOn`, `seq`, `affects`, `force` | Reword a question; a changed `rec` or `alternatives` is refused when an alternative repeats or contains the recommendation, so revise both in the same op; `commits` replaces the list and resets its confirmations; `dependsOn` replaces the prerequisites (CLI `--depends`, `--depends none` clears) and is validated like `add`: every id known, not the question itself, none already depending on it. The change is a history line and bumps no `contentRev` |
| `reply` | `id`, `text`, `seq`, `kind` (`reply` by default, `rephrase`, `note`), `rec` + `why` + `affects` + `alternatives` or `keepAlternatives`, `resolution`, `handled`, `force` | Answer an ask or rephrase; `rec` revises the recommendation and needs `alternatives` (the revised ones, so the answer and the full revision land in one op) or `keepAlternatives: true` (you checked the current ones against the new recommendation), and is refused with neither or when an alternative repeats or contains it; `resolution` records the accepted reading of the question's counted `own` answer (refused when none counts), which every export gives as the answer while the user's words become its note |
| `revise` | `id`, `title`, `short`, `facts`, `basis`, `rec`, `why`, `text`, `alternatives`, `keepAlternatives`, `commits`, `dependsOn`, `group`, `seq`, `affects`, `force` | Reword a question; a changed `rec` needs `alternatives` or `keepAlternatives: true` (CLI `--keep-alternatives`) as on `reply`, and a changed `rec` or `alternatives` is refused when an alternative repeats or contains the recommendation, so revise both in the same op; the history line records whether the alternatives were rewritten or kept; `commits` replaces the list and resets its confirmations; `dependsOn` replaces the prerequisites (CLI `--depends`, `--depends none` clears) and is validated like `add`: every id known, not the question itself, none already depending on it. The change is a history line and bumps no `contentRev`; `group` moves the question to another existing group (an unknown one is refused), logs a history line the same way, and warns when the old or new group's summary predates the move |
| `note-reply` | `text`, `seq` | Answer a note in Notes to Claude; with no `seq`, post a closing probe there |
| `add`, `add-round`, `group` | `question` (+ `repoint`); `round`, `meta`, `groups`, `questions`, `visuals` (+ `repoint`); `id`, `title`, `summary`, `dependsOn` | New questions and groups; writing a `summary` records the group's current question ids as `summaryOf`, and the page marks the summary Stale once the members differ, so rewrite the summary after adding questions. A question with `supersedes: X` prints the live questions whose `dependsOn` names X; with `repoint` (CLI `--repoint`) it moves each to the new id, drops the old id instead from any the new question itself depends on (a move would make a cycle), and lists what changed, so no live question still names the superseded one. Prefer `revise` with `dependsOn` and `commits` over archive-and-replace: the id and the user's thread stay |
| `add`, `add-round`, `group` | `question` (+ `repoint`); `round`, `meta`, `groups`, `questions`, `visuals` (+ `repoint`); `id`, `title`, `summary`, `dependsOn` | New questions and groups; writing a `summary` records the group's current question ids as `summaryOf`, and the page marks the summary Stale once the members differ, so rewrite the summary after adding questions. A question with `supersedes: X` prints the live questions whose `dependsOn` names X; with `repoint` (CLI `--repoint`) it moves each to the new id, drops the old id instead from any the new question itself depends on (a move would make a cycle), and lists what changed, so no live question still names the superseded one. Prefer `revise` with `dependsOn`, `commits` and `group` over archive-and-replace: the id and the user's thread stay |
| `group-order` | `ids` | Reorder the groups: every existing group id once, in the new order (a list that leaves one out, invents one or repeats one is refused). The Groups rail follows it; group `dependsOn` locking does not change |
| `meta` | `set` (`title`, `eyebrow`, `stages`, `next`, `repo`) | Merge into `meta`; other meta keys stay |
| `archive` | `ids`, `why` | Take off-path questions out of the open count; a question whose prerequisite is archived or superseded is not Blocked and stays eligible for accept-all (the page labels the prerequisite `archived` or `superseded by <id>`) |
| `replace-visual` | `visual` | Swap in a full visual object for the top-level visual with the same id; an unknown id is refused |
Expand All @@ -84,7 +85,7 @@ When the first wake prompts for permission, offer the user one allow rule, `Bash

One-line fields (a title, `short`, `rec`, an alternative, a commitment, a hold, a status, an activity entry, a reason) are capped at 500 characters and markdown fields (`facts`, `basis`, `why`, a note, a summary, a restatement section) at 20000; a longer one is refused and nothing is written.

A `rec` needs `affects`: question ids, or `"none"` (R2). A recommendation change marks each question named in `affects`, and each direct dependent of the revised question, that holds a decision as `Upstream changed`: stale, kept decision visible, the revised id named on its card. One level only; the transitive cascade stays deferred. Answering the question again clears it (Reconfirm is choice 1), as does `record-terminal`; a question with no decision has nothing to mark. A history line carries `pageSeq`, the page's seq at the change, so an answer that landed before it is marked and one after it is not, and `rev`, which orders it against a `record-terminal` answer written in the same second. `reply` with `rec` and `revise` with `rec` refuse when the question has a live user event newer than `seq` (an undo or a withdrawn event does not count); read that event before passing `"force": true`. `reply`'s `handled: N` marks every event with seq at or below N handled, including other questions' events; prefer `handle` with explicit seqs.
A `rec` needs `affects`: question ids, or `"none"` (R2). A recommendation change marks each question named in `affects`, and each direct dependent of the revised question, that holds a decision as `Upstream changed`: stale, kept decision visible, the revised id named on its card. One level only; the transitive cascade stays deferred. Answering the question again clears it (Reconfirm is choice 1), as does `record-terminal`; a kept accept gets no Reconfirm once the question's own recommendation changed after it, so the user picks `Rec` (the revised text, with the old one under Previous) or another choice; a question with no decision has nothing to mark. A history line carries `pageSeq`, the page's seq at the change, so an answer that landed before it is marked and one after it is not, and `rev`, which orders it against a `record-terminal` answer written in the same second. `reply` with `rec` and `revise` with `rec` refuse when the question has a live user event newer than `seq` (an undo or a withdrawn event does not count); read that event before passing `"force": true`. `reply`'s `handled: N` marks every event with seq at or below N handled, including other questions' events; prefer `handle` with explicit seqs.

```json
{"ops": [
Expand All @@ -95,7 +96,7 @@ A `rec` needs `affects`: question ids, or `"none"` (R2). A recommendation change

### Status, activity and holds

The page header shows a Claude line: the `set-status` text with its age while one is set, else the working text ("Claude is working on Qn") while events are unhandled, else the newest Activity entry. The Activity panel lists entries newest first. Each write (an `apply` or a CLI command) that includes `reply`, `note-reply`, `revise`, `add`, `add-round`, `archive`, `record-terminal`, `wait`, `confirm-commitments` or `restate` writes one summary entry on its own; `handle`, `meta`, `group` and `set-status` write none, and an `activity` op writes its own entry. Add an `activity` op for work the page cannot see: a ledger update, a gate run, research dispatched or returned. A status stays until it is replaced or cleared. The page splits the panel into `Needs you` (a new round, a Notes reply, a restatement to confirm, the finish, and entries naming a question that still waits on the user: a reply to their ask, a hold on their answer) and a collapsed `Log` (the rest: ledger writes, gate runs, revisions, research holds). Only `Needs you` entries raise the in-page notice that goes to them, the Activity badge, a dismissible toast when one arrives (the finish reads `Interview complete`) and the count in the tab title while the tab is hidden, so the user sees those without a reload; log entries wait in the panel. A restatement still awaiting a verdict keeps the notice over a later Notes entry. The toast offers a browser notification for a hidden tab; the page asks the browser for permission only when the user clicks that offer. A `set-status` text such as `Done: the Brief is written` stays on the Claude line while the page is offline. A `context` op shows a badge in the header (`72% amber`: `zone` is the session's own word for the band, at most 40 characters). The badge is a display only: it adds no `Needs you` entry, toast or title count. A `handoff` (one line naming the handoff file or resume instruction) shows a banner, `The interview continues: <text>`, which the browser also keeps, so a tab that loads while the server is unreachable still shows it; `clear` removes the badge and the banner. When the session knows its context level, send a `context` op with `percent` and `zone`; when it hands off, send `context` with `handoff` naming where the interview continues. Do not write the level into `set-status`. The `finish` op opens a dismissible modal (Brief path, `next` or else `meta.next`, "You can close this tab"), and the page holds it, so a stopped server reads `Interview finished: server stopped`. The browser also keeps the finish text, so a tab that loads while the server cannot be reached still shows it; a new server on the data dir resumes the interview and drops it, the context badge and the handoff banner. A dropped connection with no finish reads `Connection lost: check the terminal`, and a server that restarted on the same port while the tab was open reads `Server restarted: reload`, since its new token refuses the old tab's saves.
The page header shows a Claude line: the `set-status` text with its age while one is set, else the working text ("Claude is working on Qn") while events are unhandled, else the newest Activity entry. The Activity panel lists entries newest first. Each write (an `apply` or a CLI command) that includes `reply`, `note-reply`, `revise`, `add`, `add-round`, `archive`, `record-terminal`, `wait`, `confirm-commitments` or `restate` writes one summary entry on its own; `handle`, `meta`, `group`, `group-order` and `set-status` write none, and an `activity` op writes its own entry. Add an `activity` op for work the page cannot see: a ledger update, a gate run, research dispatched or returned. A status stays until it is replaced or cleared. The page splits the panel into `Needs you` (a new round, a Notes reply, a restatement to confirm, the finish, and entries naming a question that still waits on the user: a reply to their ask, a hold on their answer) and a collapsed `Log` (the rest: ledger writes, gate runs, revisions, research holds). Only `Needs you` entries raise the in-page notice that goes to them, the Activity badge, a dismissible toast when one arrives (the finish reads `Interview complete`) and the count in the tab title while the tab is hidden, so the user sees those without a reload; log entries wait in the panel. A restatement still awaiting a verdict keeps the notice over a later Notes entry. The toast offers a browser notification for a hidden tab; the page asks the browser for permission only when the user clicks that offer. A `set-status` text such as `Done: the Brief is written` stays on the Claude line while the page is offline. A `context` op shows a badge in the header (`72% amber`: `zone` is the session's own word for the band, at most 40 characters). The badge is a display only: it adds no `Needs you` entry, toast or title count. A `handoff` (one line naming the handoff file or resume instruction) shows a banner, `The interview continues: <text>`, which the browser also keeps, so a tab that loads while the server is unreachable still shows it; `clear` removes the badge and the banner. When the session knows its context level, send a `context` op with `percent` and `zone`; when it hands off, send `context` with `handoff` naming where the interview continues. Do not write the level into `set-status`. The `finish` op opens a dismissible modal (Brief path, `next` or else `meta.next`, "You can close this tab"), and the page holds it, so a stopped server reads `Interview finished: server stopped`. The browser also keeps the finish text, so a tab that loads while the server cannot be reached still shows it; a new server on the data dir resumes the interview and drops it, the context badge and the handoff banner. A dropped connection with no finish reads `Connection lost: check the terminal`, and a server that restarted on the same port while the tab was open reads `Server restarted: reload`, since its new token refuses the old tab's saves.

A `wait` holds a question in one of two ways. The page labels them as follows:

Expand Down Expand Up @@ -205,7 +206,7 @@ Rules R-A to R-K:
| R-F | Correct an obvious speech-to-text error openly, never silently | skill |
| R-G | Any skill can open the surface; the stage is a tag; a mid-implementation pause opens a session seeded with `import-ledger` | skill, `import-ledger` |
| R-H | Mirror terminal answers onto the page with `record-terminal`; session-recorded decisions follow R-K | skill |
| R-I | Every question has at least two distinct alternatives; whenever a recommendation changes, revise the alternatives in the same op, so no alternative repeats or contains the new recommendation (case and trailing punctuation folded) | `add`, `add-round`, `apply` (count); `reply --rec`, `revise` (collision) |
| R-I | Every question has at least two distinct alternatives; whenever a recommendation changes, revise the alternatives in the same op, or state with `keepAlternatives` that the current ones were checked against it, so no alternative repeats or contains the new recommendation (case and trailing punctuation folded) | `add`, `add-round`, `apply` (count); `reply --rec`, `revise --rec` (alternatives or `keepAlternatives`); `reply --rec`, `revise` (collision) |
| R-J | Emoji markers follow the plugin's emoji option on the page too | page, from `meta.emojiMarkers` |
| R-K | When the session records or revises a ledger decision, mirror it onto the page with `record-terminal` in the same wake | skill |

Expand Down
Loading
Loading