githubnext/chopinPublic

Let's compose rich plans together

Run the Planner as a full Atomic session with questions routed to Decisions #215

Mergedlavaman131merged 12 commits intomainfromrefs/pull/215/head
ConversationFiles changed
86 files changed+4722-402
StackedSplit
AGENTS.md+18 −5
README.md+7 −4
apps/server/src/agent/planner.ts+23 −6
apps/server/src/auth/session.test.ts+20 −0
apps/server/src/auth/session.ts+10 −0
apps/server/src/chat/address.ts+2 −1
apps/server/src/chat/invoke.test.ts+411 −0
apps/server/src/chat/limits.ts+1 −0
apps/server/src/chat/service.ts+125 −24
apps/server/src/comments/service.ts+18 −2
apps/server/src/config.test.ts+23 −46
apps/server/src/config.ts+3 −0
apps/server/src/conversation-plan/job-context.ts+2 −1
apps/server/src/harness/atomic.contract.test.ts+15 −4
apps/server/src/harness/atomic/adapter.test.ts+22 −4
apps/server/src/harness/atomic/adapter.ts+79 −31

The session closes after the “workflow started” reply, so later workflow questions never reach Decisions. Could we track owned workflows through `workflow_lifecycle` in `atomicTurnExtension`, then wait for them here before ending the stream or clearing `current`? Cancellation should stop those workflows and release the wait.

This needs changes in a few places, so there isn't a safe single-line replacement. Please cover the case where a workflow asks a question after the initial reply finishes.

apps/server/src/harness/atomic/adapter.ts:612head2f68ffa
Author

Agreed, that was a real gap. It's fixed in the PR stacked on this one, #230, using a slightly different approach. Instead of holding the stream open until the workflow finishes, Chopin keeps the atomic Planner session and its owner binding after the turn whenever the session still owns live or paused workflow runs. It tracks them through Atomic's workflow activity stream and `workflow_lifecycle` events. So the chat turn ends normally, but later questions from the workflow still reach Decisions through the same session, and the next turn reuses it.

Cancellation is handled too: Stop Planner pauses the session's runs through `session.workflows`, and Resume Planner resumes them. The session is let go when its runs end, the owner binding ends, or the document closes. Covered by `a Planner that still owns workflow runs outlives its turn, pauses, resumes, and is let go when they finish`, plus a live run where the workflow asked its intent review well after the launch reply and was answered in Decisions.

Assistant-workflow: inline Assistant-verification: bun test passed: retention, pause/resume, and release tests in #230 Co-authored-by: Alex Lavaee <lavaman131@github.com>

apps/server/src/harness/atomic/adapter.ts:612head2f68ffa
apps/server/src/harness/atomic/checkout.test.ts+37 −0
apps/server/src/harness/atomic/checkout.ts+32 −0
apps/server/src/harness/atomic/full.test.ts+308 −0
apps/server/src/harness/atomic/full.ts+17 −0
apps/server/src/harness/atomic/human-input.test.ts+1207 −0
apps/server/src/harness/atomic/human-input.ts+137 −0
apps/server/src/harness/atomic/workspace.ts+67 −0
apps/server/src/harness/harnesses.ts+2 −0
apps/server/src/harness/pi/model-stub.ts+4 −0
apps/server/src/harness/session.test.ts+108 −1
apps/server/src/harness/session.ts+24 −2
apps/server/src/main.ts+44 −11
apps/server/src/mcp.ts+103 −0
apps/server/src/mcp/documents.test.ts+31 −0
apps/server/src/mcp/hosted.test.ts+142 −0
apps/server/src/mcp/hosted.ts+56 −0
apps/server/src/mcp/invoke.test.ts+109 −0
apps/server/src/plan/anchors.test.ts+7 −1
apps/server/src/plan/edit.test.ts+3 −1
apps/server/src/plan/edit.ts+8 −0
apps/server/src/plan/passages.test.ts+7 −1
apps/server/src/plan/room.test.ts+51 −58
apps/server/src/plan/room.ts+68 −1
apps/server/src/plan/service.ts+1 −0
apps/server/src/questions/prose-1.test.ts+7 −1
apps/server/src/questions/record-fields.ts+12 −2
apps/server/src/questions/record-types.ts+1 −1
apps/server/src/questions/record-validation.ts+12 −6
apps/server/src/questions/service-append-option.ts+11 −0
apps/server/src/questions/service-ask.ts+52 −3
apps/server/src/questions/service-cancel.ts+111 −48
apps/server/src/questions/service-definition.ts+2 −2
apps/server/src/questions/service-submit.ts+14 −0
apps/server/src/questions/service.ts+1 −1
apps/server/src/questions/store-types.ts+1 −1
apps/server/src/questions/store.ts+4 −2
apps/server/src/rooms.test.ts+35 −0
apps/server/src/rooms.ts+18 −0
apps/server/src/testing/config.ts+54 −0
apps/server/src/testing/decisions.ts+156 −0
apps/server/src/testing/peer.ts+59 −0
docs/architecture.md+5 −2
docs/authentication.md+11 −0
docs/hosted-agent.md+137 −30
docs/local-agent-mcp.md+82 −9
docs/self-hosting.md+58 −10
packages/dialect/src/convert.test.ts+31 −0
packages/dialect/src/dialect.test.ts+27 −0
packages/dialect/src/dialect.ts+9 −6
packages/dialect/src/index.ts+1 −0
packages/dialect/src/limits.ts+10 −4
packages/dialect/src/nodes/questionnaire-fields.ts+9 −2
packages/dialect/src/nodes/questionnaire-mdx.ts+21 −7
packages/dialect/src/validate.ts+7 −2
packages/editor/src/decision-state.test.ts+8 −0
packages/editor/src/decision-state.ts+1 −1
packages/editor/src/decisions.test.tsx+46 −18
packages/editor/src/widgets/questionnaire.tsx+15 −0
packages/protocol/question.d.ts+16 −4
packages/question/src/answer.ts+4 −1
packages/question/src/draft.ts+5 −3
packages/question/src/limits.ts+6 −1
packages/question/src/question.test.ts+62 −3
packages/question/src/react/question-view.test.ts+51 −0
packages/question/src/react/question-view.tsx+29 −6
packages/question/src/react/questionnaire-controller.ts+5 −2
packages/question/src/react/use-questionnaire.test.ts+95 −1
packages/question/src/schema.ts+66 −16
scripts/design-contract/exceptions/dynamic-editor.json+2 −2
scripts/design-contract/exceptions/dynamic-packages.json+1 −1

Comments on earlier or unavailable lines

People need to see the supplied mockup or code before choosing. Including the preview in the description gives Decisions the missing content without changing the option labels or answers.

```suggestion description: [option.description, option.preview] .filter(value => value !== undefined) .join("\n\n"), ```

apps/server/src/harness/atomic/human-input.ts:39headbaf790b

A long answer can make the saved document too large for the Planner to edit afterwards. Check it on a temporary copy first; if it doesn't fit, the real document stays unchanged and the question stays open. I checked this against the oversized-answer reproduction.

In `submit()`, replace the call to `room.projectAnswer(plan.document, msg.id, answers, settled)` with:

```ts let preview = await room.create(room.project(plan.document)); try { room.projectAnswer(preview, msg.id, answers, settled); room.validate(room.project(preview)); } finally { preview.doc.destroy(); } mutation = room.projectAnswer(plan.document, msg.id, answers, settled); ```

apps/server/src/questions/service.ts:138headbaf790b
Author

Confirmed and fixed in 6e7685e with your preview check. To reproduce it: with about 100 KB of prose in the document, four 50 KB edits (each under the 64 KiB edit limit) built an answer that saved a 302 KB document, past the 256 KiB limit, after which a Planner edit was refused. Now the submit is refused, the document is unchanged, and the question stays open. The new test fails without the check and passes with it.

Assistant-workflow: inline Assistant-verification: fail-before/pass-after passed: oversized answer refused with the document unchanged and the question open Co-authored-by: Alex Lavaee <lavaman131@github.com>

apps/server/src/questions/service.ts:138headbaf790b
Author

Applied in 6e7685e. The preview now follows the description, and the label and the returned answer are unchanged. The test checks an option with a preview and one without.

Assistant-workflow: inline Co-authored-by: Alex Lavaee <lavaman131@github.com>

apps/server/src/harness/atomic/human-input.ts:39headbaf790b

Let's leave room for the date and ‘expired’ label before accepting a question. I reproduced a question fitting when it was asked, then pushing the document over its limit when it timed out. The Planner then refused a normal edit.

This suggestion reserves that room for new questions. It is only the first step: later text edits, added options, and new questions must also keep enough room for every still-open question to expire. Please add a test that makes those later edits, lets the questions expire, and checks that the Planner can still edit afterwards.

```suggestion export function fitsQuestionnaires(target: Document, values: Questionnaire[]): boolean { let tree = parse(project(target)); let at = new Date().toISOString(); // Reserve the metadata expiry adds to every card in this batch. tree.children.push(...values.map(value => questionnaireElement({ ...value, status: "expired", at }) )); return Buffer.byteLength(serialize(tree)) <= limits.MAX_SOURCE_BYTES; } ```

apps/server/src/plan/room.ts:443–447headfb57aa9
Author

Fixed in b7994a8, starting with your suggestion. Asking a question now measures it, and every question already open, as if expired. The same reserve then bounds every later change: a Planner edit or replacement, a person's text edit, an added option, an accepted comment's decision, and an answer that settles one question while others stay open. A change that would leave too little room is refused and the document stays as it was. Only growth is refused, so deleting or trimming text still works when the document is already inside the reserve.

The test you asked for walks all of those in one sequence: each change is driven to just under the limit and refused, then a person's growing edit is refused while a shrinking and a fitting one are accepted. Finally every open question expires within the limit and a normal Planner edit succeeds. Reverting any one of the seven guards on its own fails it.

Assistant-verification: fail-before/pass-after passed: reverting each of the 7 reserves fails the combined test; bun test 1894 pass, 0 fail

Assistant-workflow: goal (run 6091839b-867c-4965-aec9-b583f62d4b1f) Co-authored-by: Alex Lavaee <lavaman131@github.com>

apps/server/src/plan/room.ts:443–447headfb57aa9