Skip to content

Give each decision option a visual of its own, and let a Planner store the images it makes - #750

Merged
lavaman131 merged 2 commits into
mainfrom
feat/decision-option-previews
Oct 31, 2026
Merged

lavaman131 merged 2 commits into
mainfrom
feat/decision-option-previews

Conversation

@lavaman131

Copy link
Copy Markdown
Collaborator

Closes #749.

Decision options can now carry their own visual, so a member can compare the choices by looking at them, and a Planner can make the pictures.

What changed

An option's preview is its own field, end to end. ask_user_question (Atomic), the input request of an attached Planner, and the Planner's own ask, open_decision and open_decisions tools keep an option's preview as a field of its own. It is Markdown, at most 16,000 characters, stored verbatim, and travels through the stored definition, the document source (an Option attribute), the projection into the plan and the answer, which still echoes the chosen option's preview. The bridge no longer joins it onto the description. A card saved before this, whose preview was joined into its description, looks the same.

Every surface frames it under the option, in the option's own column: the Decisions tab, the inline document card, the phone sheet, answered cards (which keep every option's visual, the chosen one marked) and the decision page. It holds whatever the card's Markdown draws: the document's own images, a Mermaid fence (bounded, "Show whole diagram"), code, and, in a preview only, GitHub pipe tables.

  • A picture is held to the column and about a third of a screen (less on a phone) and opens large from its own button, in the editor's existing lightbox, without choosing the option.
  • Beside criteria an option shows a small thumbnail and leaves other content out, so the matrix keeps its shape.
  • An option is still one radio named by its label and description; the visual is a group named "Preview of ", and every image keeps its alt text and a "View larger" name. A card without previews keeps its markup and names.

The Planner can make the pictures. upload_plan_image stores a PNG, JPEG, WebP or GIF (at most 1 MiB, required alt text) in the document's images and returns the ![alt](/images/…) Markdown for a card body or an option's preview. It reuses the image storage, the upload_image checks and the document-scoped image route. It works only during a live Planner turn, never for a background job or an archived document, and never reads a path on the Planner's side (a remote Planner sends base64). The Planner's instructions now say members decide by looking and to give each option of a comparison a visual of the same kind.

A connected Planner can detect all of it. decision-option-previews is a new agent connection feature next to run-stage-deltas: a client names it in Chopin-Agent-Features, attached.features lists it when supported, and upload_plan_image is in a turn's tools only for a client that asked. The package client asks for both. A server without it joins a preview onto the description, as before. Documented in docs/agent-connection.md ("Option previews") and docs/decisions.md.

Screenshots

From the e2e run (CHOPIN_SHOTS):

Inline card with picture previews

Inline card with picture previews

Decisions tab

Decisions tab

Decision page

Decision page

Answered card keeps every option's visual

Answered card keeps every option's visual

Phone sheet

Phone sheet

Opened large

Opened large

Opened large on a phone

Opened large on a phone

Mermaid diagram and table previews

Mermaid diagram and table previews

Mermaid and table previews on a phone

Mermaid and table previews on a phone

Criteria row thumbnail

Criteria row thumbnail

Tests

  • Unit: schema and bounds, the document round trip (a diagram and an image, indentation kept), the Atomic bridge, the rendering on every card surface, the image tool's input checks, the room's upload access (live turn, archived document, background job, attribution), and the feature-gated tool list.
  • Decisions e2e (e2e/decision-option-previews.e2e.ts): picture and Mermaid previews on every surface, large view and keyboard, a legacy card, a criteria row.
  • Remote Planner e2e (e2e/planner-attachment.e2e.ts): a real attached agent stores two images through the tool, asks a question using them in its options and body, sees them drawn, checks that only this document serves the bytes, and gets the chosen option's preview back.
  • Passing: bun run types, bun run ci (design contract, impeccable detector), bun test in apps/server (3961), apps/web (858) and the packages, and 76 Decisions e2e tests plus the 11 above.

Notes

  • The design contract needed only the source-hash renewal of two reviewed exceptions in files this change edits (question-view.tsx, questionnaire.tsx); no new exception.
  • Tables are enabled only inside an option's preview (micromark-extension-gfm-table and mdast-util-gfm-table, already in the lockfile through the editor), so existing card text renders as before.
  • Not covered: generating the images (a Planner does that with its own model), and a hard limit on images per option (rendering is bounded per option instead; one visual per option reads best).

…diagrams and tables

A member who has to choose between two options should be able to compare
them by looking, not by reading two paragraphs that differ in one clause.
An option's `preview` is now its own field from the question a Planner
asks to every card surface, and it is drawn as a framed Markdown block
under the option's label and description.

The field. `Question.Option` and the document's `Option` node carry an
optional `preview`, at most 16,000 characters, kept verbatim for a host
dialog and left out when empty. It round-trips through the document source
as an `Option` attribute, indentation included, and through the stored
definition, the projection into the plan, the backfill comparison and the
definition shape the server compares before it publishes. Atomic's
`ask_user_question` bridge no longer joins the preview onto the end of the
description: it passes both, and the answer still echoes the chosen option's
preview. The Planner's own `ask`, `open_decision` and `open_decisions`
input takes it too. A card saved before this, whose preview was joined into
its description, is untouched and looks the same.

The rendering. `QuestionView` is the one place every surface reads an
option from, so the Decisions tab, the inline card, the phone sheet,
answered cards and the decision page all show the frame, and an answered
card keeps every option's visual with the chosen one marked. The frame is
the inset tone with a passive edge, in the option's own column. It holds
whatever the card's Markdown renderer draws: a Mermaid fence through the
document's renderer at a bounded height, the document's own images, code,
and, in a preview only, GitHub pipe tables. A picture is held to the column
and a third of a screen, less on a phone, and a loaded image opens large
from its own button, in the editor's existing lightbox, without picking the
option it sits in. An image that is not this document's shows its alt text,
as before. Beside criteria an option shows a small thumbnail and leaves
other content out of the row, so the matrix keeps its shape.

Accessibility. An option with a visual is still one radio button named by
its label and description, not by every word a diagram carries; the visual
is a group of its own named "Preview of <label>", and every image keeps its
alt text and a "View larger" name. A card without previews keeps the
markup and the accessible names it had.

Verification. A unit test covers the schema, the document round trip, the
bridge and the rendering on every card surface. Decisions e2e covers a
picture and a Mermaid preview on the inline card, the Decisions tab, the
decision page, the phone sheet and an answered card, the large view and
the keyboard, a legacy card, and a criteria row, with screenshots of each.

Assistant-model: Claude Opus 5.5 (fast)
Assistant-workflow: inline
Assistant-verification: bun test on this commit alone passed: apps/server 3952, apps/web 858, packages/question 214, packages/dialect 173
Assistant-verification: bun run types passed on this commit alone; bun run ci passed with the whole change, including the design contract and the impeccable detector
Assistant-verification: bun run e2e passed with the whole change: the new decision-option-previews spec and the other Decisions specs, 76 passed
Co-authored-by: Alex Lavaee <lavaman131@github.com>
Decision options can carry an image, but a Planner that generates one, from
codemode or any image model, had nowhere to put it: only MCP clients could
upload to a document's images. This gives every Planner turn a tool to do
that, tells the Planner how to use it, and lets a connected Planner learn
whether the server supports all of it before it relies on it.

`upload_plan_image`. A Planner turn can store a PNG, JPEG, WebP or GIF of at
most 1 MiB in the document's own images, with required alt text of at most
200 characters, and gets back its path and the `![alt](/images/…)` Markdown
for a card body or an option's `preview`. The bytes get the same checks
`upload_image` makes, the same bytes give the same path, and the image loads
only for people who can read this document. The server never reads a path on
the Planner's side: a remote Planner encodes its own file. The room stores it
only during a live Planner turn, never for a background job, never in an
archived document, and attributes it to the member the turn is for, or to the
Planner's owner for a turn nobody asked for. It is a write tool, so the
background job profiles exclude it.

Capability. `decision-option-previews` joins `run-stage-deltas` as an agent
connection feature: a client names it in `Chopin-Agent-Features`, and
`attached.features` lists it when the server supports it. A turn's host tools
include `upload_plan_image` only for a client that asked, so an older client
is never offered a tool it cannot use. The package client asks for both
features. A server from before the feature joins a preview onto the option's
description and offers no tool, as before.

Planner guidance. The Planner's `ask`, `open_decision` and `open_decisions`
tools take `preview` per option, and its instructions tell it members decide
by looking: give each option of a comparison a visual of the same kind, a
small Mermaid diagram, a table or an image it made, and keep the description
as the caption.

Verification. Unit tests cover the tool's input checks, the room's upload
access (live turn, archived document, background job, attribution), and the
feature-gated tool list. The remote Planner e2e attaches a real agent, stores
two images through the tool, asks a question whose options and body use them,
sees them drawn, checks that only this document serves the bytes, and returns
the chosen option's preview.

Assistant-model: Claude Opus 5.5 (fast)
Assistant-workflow: inline
Assistant-verification: bun test apps/server (3961), apps/web (858), packages passed with the whole change
Assistant-verification: bun run types and bun run ci passed with the whole change, including the design contract and the impeccable detector
Assistant-verification: bun run e2e passed: planner-attachment with decision-option-previews, 11 tests, and ten other Decisions specs, 76 tests
Co-authored-by: Alex Lavaee <lavaman131@github.com>
@lavaman131
lavaman131 merged commit 366965d into main Oct 31, 2026
24 checks passed
@lavaman131
lavaman131 deleted the feat/decision-option-previews branch October 31, 2026 05:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Decision cards: a rendered preview per option, and images the Planner made

1 participant