Skip to content

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

Open
lavaman131 wants to merge 11 commits into
atomic-harnessfrom
atomic-full-planner
Open

lavaman131 wants to merge 11 commits into
atomic-harnessfrom
atomic-full-planner

Conversation

@lavaman131

Copy link
Copy Markdown
Collaborator

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

if (!run.stopped) {
run.emit({ type: "stream-start", modelId: agent.model?.id });
await agent.prompt(text, { expandPromptTemplates: false });
await agent.prompt(text, { expandPromptTemplates: !!full });

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

})),
}],
}));
// Verbatim host input has no per-field limits, so the document bounds it.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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:

			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);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

question: workflow ? `${question.question}\n\n${workflow}` : question.question,
options: question.options.map(option => ({
label: option.label,
description: option.description,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

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

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Opt in with ATOMIC_PLANNER=full only under local authentication and the Atomic
harness. Verify each Planner checkout's origin before enabling Atomic coding
tools, builtins, and operator resources. Unregistered worker sessions and
Planner sessions without a verified checkout retain the isolated boundary.

Bind Chopin's Decisions to Atomic HostInput rather than intercepting tools.
Map questionnaires and dialogs, preserve raw answers, label workflow requests,
and withdraw pending cards on abort or timeout after durable persistence.
Document the operator-level shell and filesystem trust change.

Assistant-model: Claude Opus 5.5
Assistant-workflow: goal (run 98acf681-3730-4957-b885-0d70e0e6b32a)
Assistant-verification: bun test passed: 1706 pass, 2 PostgreSQL skips, 0 fail across 193 files
Assistant-verification: bun test apps/server/src/harness passed: 141 tests, including full Atomic SDK tools, resources, cwd, HostInput and isolated fallback
Assistant-verification: bun run types passed: all workspaces and E2E TypeScript checks
Assistant-verification: bun run fix passed: formatting inspected; no errors and one existing lint warning
Assistant-verification: bun run ci passed: formatting, lint, tokens, typography and design checks; no new design findings
Assistant-verification: isolation mutation checks passed: builtins on caused 16 failures, skill discovery caused 2, and plain-turn result tool caused 9; all mutations reverted byte-for-byte
Assistant-verification: relative documentation link check passed: 38 links and anchors resolved with exact casing
Assistant-verification: qlty smells passed: no duplication findings; complexity diagnostics match existing adapter closure patterns and are recorded in implementation notes
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"
Co-authored-by: Alex Lavaee <lavaman131@github.com>
Wrap the rename, archive and restore result unions in object schemas without changing their branches. Check every exported tool and the fully enabled MCP tools/list response.

Assistant-model: Claude Opus 5.5
Assistant-workflow: goal (run 98acf681-3730-4957-b885-0d70e0e6b32a)
Assistant-verification: focused MCP tests passed: 81 tests, 450 assertions; the new invariant failed before the fix on rename_document
Assistant-verification: bun run types passed: all workspaces and E2E
Assistant-verification: bun run fix and bun run ci passed: formatting inspected, no errors and one existing lint warning
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"
Co-authored-by: Alex Lavaee <lavaman131@github.com>
Expose invoke_planner only for the full local Atomic mode. Resolve the
existing document and require a live browser login for the MCP caller's
GitHub identity; the bearer never supplies Planner ownership. Verify an
explicit checkout before posting, and retain it only for its own turn.

Persist the verbatim instruction as a member message before returning the
canonical document URL. Keep a server-opened room alive through the turn
and queue, without changing ordinary socket-only eviction. Document the
local shell and filesystem trust boundary and refusal codes.

Assistant-model: Claude Opus 5.5
Assistant-workflow: goal (run 98acf681-3730-4957-b885-0d70e0e6b32a)
Assistant-verification: bun test passed: 1718 pass, 2 PostgreSQL skips, 0 fail, 7344 assertions across 196 files
Assistant-verification: focused MCP tests passed: 87 tests, including capability gating, input bounds, output schemas, session ownership and checkout verification
Assistant-verification: harness tests passed: 141 tests; full Atomic and default isolated behavior remain covered
Assistant-verification: bun run types passed: all workspaces and E2E
Assistant-verification: bun run fix and bun run ci passed: formatting inspected; no errors, one existing lint warning and no new design findings
Assistant-verification: relative documentation links passed: 26 targets and anchors resolved with exact casing
Assistant-verification: qlty smells completed: guard-return complexity and existing archive/restore duplication recorded in implementation notes
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"
Co-authored-by: Alex Lavaee <lavaman131@github.com>
Return a written answer as Atomic's custom kind only where its own dialog
accepts typed text, and as typed chat on multi-select or preview questions,
so Atomic's validator accepts every answer a member can give.

Hold verbatim host input only to aggregate bounds. Normalization and the
shared draft skip per-field counts and lengths for verbatim questions, the
dialect bounds questionnaire text by the source size, and an ask that would
take the document past 256 KiB fails before any card or record exists. The
Planner's ask tool keeps its per-field limits.

Read the Atomic checkout and timeout settings only in full mode, treating
empty values as unset.

Assistant-model: Claude Opus 5.5
Assistant-workflow: goal (run 98acf681-3730-4957-b885-0d70e0e6b32a)
Assistant-verification: fail-before/pass-after passed: the 9 new tests failed with source changes stashed (InvalidHostInput, per-field QuestionErrors, config throw) and 93 of 93 passed after
Assistant-verification: bun test passed: 1727 pass, 2 PostgreSQL skips, 0 fail, 7393 assertions across 196 files
Assistant-verification: focused bun test passed: 294 tests in harness, question, dialect and config, including a full Atomic session answered through Decisions
Assistant-verification: aggregate-bound mutation check passed: disabling the document fit check made the oversize dialog test time out; source restored byte-for-byte
Assistant-verification: bun run types passed: all workspaces and E2E
Assistant-verification: bun run fix and bun run ci passed: formatting inspected; no errors, one existing lint warning and no new design findings
Assistant-verification: relative documentation links passed: 27 links and anchors resolved with exact casing
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"
Co-authored-by: Alex Lavaee <lavaman131@github.com>
Replacing a custom answer always deleted the current text first. On an
empty answer json-joy 17 binary-encodes that zero-length delete so the
server decodes garbage that swallows the following insert, yet still
acknowledges the edit. A paste, fill or one-character answer into the
empty box was lost, and a verbatim host question then saved "".

Delete only existing text, and skip the insert for an empty value, which
json-joy refuses: clearing a typed answer threw after its delete.

Assistant-model: Claude Opus 5.5
Assistant-workflow: goal (run 98acf681-3730-4957-b885-0d70e0e6b32a)
Assistant-verification: fail-before/pass-after passed: with the unfixed change() the 5 new controller tests failed (the server-applied draft kept custom "" after one input for verbatim and ordinary questions and a host-only paste, and clearing threw EMPTY_STRING); 12 of 12 passed after
Assistant-verification: bun test passed: 1732 pass, 2 PostgreSQL skips, 0 fail, 7402 assertions across 196 files
Assistant-verification: bun test packages/question passed: 35 pass, 0 fail
Assistant-verification: bun run types passed: all workspaces and E2E
Assistant-verification: bun run fix passed: no change to tracked files; one existing lint warning
Assistant-verification: bun run ci failed: dprint flagged only three untracked .atomic/todos files; dprint check excluding .atomic, oxlint, token, type-scale and design checks then passed
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"
Co-authored-by: Alex Lavaee <lavaman131@github.com>
HARNESS=atomic is now the only switch. Every Planner session on that
harness, local or hosted, runs as a full Atomic session: builtins,
default coding tools, the operator's Atomic resources, and Chopin bound
as HostInput. The summary and research workers keep their isolated
sessions, and copilot-sdk and pi are unchanged. This supersedes the
local-only opt-in from c84fa4e. Its environment variables and
Config.atomicPlanner are gone, and nothing replaces them.

A checkout now comes only from invoke_planner. On the atomic harness it
is verified against the document's origin before anything is posted,
then remembered in memory for that channel. Every later Planner session
re-verifies it before use, whether it starts from the browser or MCP.
Without a remembered checkout, the session runs with the same tools in
an empty 0700 directory created for that channel alone and removed at
shutdown. The Planner's instructions say which case applies.

Host input expires after a fixed 30 minutes. The questionnaire record
and question:resolved now carry an "expired" status. The card stays in
the document with status="expired" and is listed among the resolved
Decisions, with a note that nobody answered and the Planner will use its
best judgement. Atomic receives no answer, and late submissions are
refused. An abort or member cancel still withdraws cards as before.

invoke_planner is offered for every harness and auth mode. The posted
message is attributed to the caller. The turn runs under the channel's
existing owner, or the caller's live browser login claims ownership, or
the call is refused before anything is posted. Other harnesses ignore
the checkout.

Assistant-model: Claude Opus 5.5
Assistant-workflow: goal (run 98acf681-3730-4957-b885-0d70e0e6b32a)
Assistant-verification: bun test passed: 1746 pass, 2 PostgreSQL skips, 0 fail, 7619 assertions across 196 files
Assistant-verification: focused bun test (harness, mcp, chat, config, question, protocol) passed: 390 pass, 0 fail, 1821 assertions across 38 files
Assistant-verification: worker isolation mutations passed: BUILTINS_OFF set true failed 17 of 34, isolated skill discovery failed 2, the result tool offered on plain turns failed 11; the adapter was restored byte-for-byte
Assistant-verification: behaviour mutations passed: never registering atomic sessions, withdrawing on expiry, rejecting expired records on restore, and refusing an owner other than the caller each failed their new tests; every file was restored byte-for-byte
Assistant-verification: bun run types passed: all workspaces and E2E
Assistant-verification: bun run fix passed: formatted changed files and one untracked .atomic todo, which was restored byte-for-byte; one existing lint warning
Assistant-verification: bun run ci failed: dprint flagged only one untracked .atomic/todos file; dprint check on the tracked and new files, oxlint, token, type-scale and design checks then passed
Assistant-verification: relative-link check passed: every relative link and anchor in README.md, AGENTS.md and docs resolves with exact casing
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>
The full Planner kept the operator's settings in an in-memory manager
with no project scope, so packages a repository installs for itself
(`atomic install -l`, recorded in `.atomic/settings.json`) never loaded,
and their workflows and tools were missing from Planner turns run in that
checkout. Seed the in-memory project scope from the working directory's
`.atomic/settings.json` as trusted project settings, keep both files
unwritten, and reapply Chopin's compaction, summary, and cache overrides.

Assistant-model: Claude Opus 5.5
Assistant-workflow: inline
Assistant-verification: bun test passed: 1749 tests, 2 PostgreSQL skips, 0 fail, including a new full-Planner test that a checkout's project package contributes its tool and skill without writing either settings file
Assistant-verification: bun run types passed: all workspaces
Assistant-verification: bun run ci passed: dprint, oxlint, tokens, type scale, Impeccable baseline
Assistant-verification: local E2E failed before this fix: invoke_planner reached a full Planner whose tools lacked a package installed through the checkout's .atomic/settings.json
User-preference: HARNESS=atomic runs the Planner as a full Atomic session, so a checkout should behave like a local Atomic session in that repository
Co-authored-by: Alex Lavaee <lavaman131@github.com>
Drop the full Atomic session framing and the tool inventory from the
Planner's workspace instructions; the system prompt already lists its
tools. Keep only what the model cannot infer: questions from
ask_user_question and workflows appear as Decisions, and an expired
question means proceeding on best judgement.

Assistant-model: Claude Opus 5.5
Assistant-workflow: inline
Assistant-duration: 12m converged, estimated 10m
Assistant-verification: bun test passed: apps/server/src/harness, chat and agent, 302 pass, 0 fail
Assistant-verification: bun run types passed: all workspaces and E2E
Assistant-verification: bun run ci passed: dprint, oxlint, tokens, type scale and design checks
User-preference: Planner prompts stay neutral; no "full Atomic session" meta framing, name tools only where it adds information the system prompt lacks
Co-authored-by: Alex Lavaee <lavaman131@github.com>
Apply dprint's formatting to the Planner workspace instructions, which
the format check flagged.

Assistant-model: Claude Opus 5.5
Assistant-workflow: inline
Assistant-verification: bun run ci passed: dprint check clean after formatting
Co-authored-by: Alex Lavaee <lavaman131@github.com>
…sions

Decisions on main no longer offers a free-text custom answer: a member
writes one by adding an option. The host input bridge now returns an
added option as a written answer, as custom where Atomic's own dialog
accepts typed text and as chat otherwise, so Atomic never receives a
label it did not offer. An input or editor dialog, which has no options,
is answered by the option a member adds. The test fixture adds options
the way the client does, and the view tests check that a no-option host
card offers "Add an option" rather than a text box.

The rebase added the expired-card view to question-view.tsx and the
editor questionnaire widget. Their reviewed dynamic class and pointing
expressions are unchanged and take no data from the new code, so both
review hashes are renewed.

Assistant-model: Claude Opus 5.5
Assistant-workflow: inline
Assistant-verification: bun test passed: 1882 pass, 2 PostgreSQL skips, 0 fail, including added options returned as custom, chat, and input/editor answers
Assistant-verification: bun run types passed: all workspaces
Assistant-verification: bun run ci passed: dprint, oxlint, tokens, design contract, design record, Impeccable (no new findings)
User-preference: Rebase PRs onto the latest main and adapt to its code rather than keeping the old design
User-preference: Fit agent questions to Decisions' options format rather than adding a free-text field
Co-authored-by: Alex Lavaee <lavaman131@github.com>
…views

Edits under the per-edit limit could build an answer large enough to
push the saved document past its size limit, after which the Planner
could no longer edit it. Project the answer onto a copy and validate it
first; when it does not fit, the document stays unchanged and the
question stays open.

Decisions has no preview pane, so an Atomic option's preview (a mockup
or code) now travels in its description, and people see it before
choosing without the label or the returned answer changing.

Both reported by Maggie Appleton in review.

Assistant-model: Claude Opus 5.5
Assistant-workflow: inline
Assistant-verification: fail-before/pass-after passed: with about 100 KB of prose, four 50 KB edits built an answer that saved a 302 KB document past the 256 KiB limit; with the check the submit is refused, the document is unchanged and the question stays open
Assistant-verification: bun test passed: 1883 pass, 2 PostgreSQL skips, 0 fail
Assistant-verification: bun run types passed: all workspaces
Assistant-verification: bun run ci passed: dprint, oxlint, tokens, design contract, design record, Impeccable (no new findings)
Co-authored-by: Alex Lavaee <lavaman131@github.com>
@lavaman131
lavaman131 force-pushed the atomic-full-planner branch from 6e7685e to fb57aa9 Compare October 1, 2026 16:17
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.

2 participants