Run the Planner as a full Atomic session with questions routed to Decisions #215
- LA
## Summary
Closes #213.
Builds on and stacked on #210 (`atomic-harness`).
Makes `HARNESS=atomic` run the hosted Planner as a full Atomic session in both local and hosted configurations, with Chopin acting as the human-input host (`HostInput`) so every question routes to Decisions.
1. **Full Atomic Planner session**: - `HARNESS=atomic` always runs Planner sessions as full Atomic sessions with Atomic builtins (workflows, subagents, MCP, web access, intercom), default coding tools, and operator Atomic resources (extensions, skills, prompt templates, context files). - Atomic background workers (document summary, research synthesis, structured output) remain isolated. The `copilot-sdk` and `pi` harnesses are unchanged. - Verified repository checkout support: an explicit checkout passed to `invoke_planner` is verified against the document repository's git origin, then remembered for that channel and reused on later browser- and agent-initiated turns. Without a verified checkout, the session runs in an isolated empty working directory with the same tools. - Documents the operator-level shell and filesystem trust boundary clearly in `docs/self-hosting.md` and `docs/hosted-agent.md`.
2. **Chopin as Atomic HostInput backed by Decisions**: - Implements `HostInput` bound via `extensionBindings.humanInput`. - Maps `questionnaire` to Chopin's `ask` (Decisions) one-to-one; single-choice and multi-choice map cleanly; `confirm` and `select` become single-question questionnaires; `input` and `editor` become free-text cards. - Respects `AbortSignal` (withdraws the card) and handles timeouts: after a 30-minute timeout, the card resolves as expired with an inline notice in Decisions that nobody answered and the Planner will proceed on best judgement, while the callback returns "no answer". - Verbatim host questions and answers bypass restrictive per-field card limits while keeping aggregate document bounds. - Workflow requests are labeled with `workflowRunId` and `workflowStageId`. Unanchored batches append to the document end.
3. **`invoke_planner` MCP tool**: - Registers `invoke_planner({ id, instruction, checkout? })` across all harnesses. - Posts the instruction as a member message attributed to the caller under the channel's existing owner, or allows a caller's live browser login to claim ownership. - `checkout` verification applies to the atomic harness.
4. **MCP `outputSchema` specification compliance**: - Declares top-level `"type": "object"` output schemas for `rename_document`, `archive_document`, and `restore_document` (wrapping oneOf unions), unblocking strict MCP clients such as Atomic's zod-validated MCP client. - Adds an invariant test ensuring every MCP tool in Chopin declares a top-level `"type": "object"` output schema.
## Verification
Assistant-verification: bun test passed: 1746 pass, 2 PostgreSQL skips, 0 fail, 7619 assertions across 196 files Assistant-verification: bun run types passed: all workspaces and E2E TypeScript checks Assistant-verification: bun run ci passed: dprint, oxlint (0 errors, 1 existing warning), check-tokens, check-type-scale, and Impeccable design checks Assistant-verification: bun run e2e passed: 236 passed, 0 failed in Chromium integration suite Assistant-verification: worker isolation mutations passed: BUILTINS_OFF set true failed 17 of 34, isolated skill discovery failed 2, result tool on plain turns failed 11; all restored byte-for-byte Assistant-verification: behaviour mutations passed: session registration, expiry withdrawal, expired record rejection on restore, and owner verification each failed their new tests; all restored byte-for-byte Assistant-verification: documentation links passed: relative links and anchors in README.md, AGENTS.md, and docs resolve with exact casing
Assistant-model: Claude Opus 5.5 Assistant-workflow: goal (run 98acf681-3730-4957-b885-0d70e0e6b32a) User-preference: "Atomic's SDK is the runtime and the host supplies human input; Chopin implements HostInput rather than intercepting tools" User-preference: "In local mode the Planner may run as a full Atomic session so workflows run inside Chopin with questions in Decisions" User-preference: No extra per-harness flags; HARNESS=atomic always runs the Planner as a full Atomic session, hosted included User-preference: No extra per-harness env settings; the checkout comes from invoke_planner and the input timeout is a code constant Co-authored-by: Alex Lavaee <lavaman131@github.com>
lavaman131added-to-stacklast month on GitHub
lavaman131referenced this in Show Planner-launched workflow progress as a run card in Chat #221last month on GitHub
- MAapps/server/src/harness/atomic/adapter.ts:6122f68ffa
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.
- MAapps/server/src/harness/atomic/human-input.ts:39baf790b
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"), ```
- MAapps/server/src/questions/service.ts:138baf790b
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); ```
- MA
No review summary.
lavaman131referenced this in Keep the Planner while its workflows run, with Stop/Resume and a run card #230last month on GitHub
lavaman131base-ref-force-pushedlast month on GitHub
lavaman131head-ref-force-pushed 1f723d26c61flast month on GitHub
lavaman131head-ref-force-pushed e2654f8958f2last month on GitHub
lavaman131head-ref-force-pushed 6e7685e1ffb6last month on GitHub
lavaman131head-ref-force-pushed 6e7685e1ffb6last month on GitHub
- LAapps/server/src/harness/atomic/adapter.ts:6122f68ffa
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>
- LA
No review summary.
- LA
No review summary.
- LAapps/server/src/questions/service.ts:138baf790b
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>
- LAapps/server/src/harness/atomic/human-input.ts:39baf790b
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>
- LA
No review summary.
lavaman131head-ref-force-pushed fb57aa9e47c0last month on GitHub
- MAapps/server/src/plan/room.ts:443–447fb57aa9
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; } ```
- MA
I found one bug I'd fix before merging: a question can fit when it's added, then push the document over its size limit when it expires. I reproduced the Planner refusing a normal edit afterwards. The comment includes a tested first step and explains what else needs checking to finish the fix.
MaggieAppletonbase-ref-force-pushedlast month on GitHub
MaggieAppletonbase-ref-force-pushedlast month on GitHub
MaggieAppletonautomatic-base-change-succeededlast month on GitHub
- LAapps/server/src/plan/room.ts:443–447fb57aa9
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>
- LA
No review summary.
lavaman131requested review from MaggieAppletonlast month on GitHub
lavaman131head-ref-force-pushed 2f68ffa6d35dlast month on GitHub
lavaman131referenced adb413ed4b01last month on GitHub
- MA
No review summary.
MaggieAppletonmerged aeb8dd0b9c3flast month on GitHub
MaggieAppletonhead-ref-deletedlast month on GitHub