DSHarness Systems Dissection Pinned baseline 47f943859b · 36 verified / 0 in progress / 36 chapters
中文
Collaborative State·Chapter 15

Plan, Goal, and Todo

Three similar-looking primitives with different lifecycles

VerifiedUpstream 47f943859bScope: Analyze logged plan mode, goal revisions and continuation, todo projections, permissions, and UI presentation.

Conclusion: Plan, Goal, and Todo Are Not Three Names for One “Planning State”

DeepSeek Harness places three easily confused collaboration primitives in different lifecycles. Plan mode changes the system guidance for the next model request and records the effective boolean in the Session. Goal retains one revisable, pausable, resumable long-running completion objective and uses a separate process-local activation to decide whether work may continue automatically. Todo is a lightweight list that the model replaces wholesale while working: durable events preserve its history, but the UI-facing “standing plan” is cleared when the next Turn starts.

Entering Plan therefore does not create a Goal, creating a Goal does not synthesize Todos, and several in_progress Todo items do not make the Todo tool itself execute concurrently. All three can coexist, but none is a hidden database or scheduler for the others.

Shortest distinction

Plan answers “under which collaboration policy should the next request reason?” Goal answers “what must still be completed across autonomous rounds?” Todo answers “which concrete steps does the model currently claim are pending, active, or complete?” PLANSTATE-PLAN-STATEPLANSTATE-GOAL-TYPESPLANSTATE-TODO-TOOL

1. Three State Machines: Authority, Lifetime, and Driver

PrimitiveAuthoritative factNon-durable stateWho advances itWhen it ends or disappears
PlanLast plan/mode { active }Pending intent awaiting a Step boundaryHuman /plan; model via approved exit_plan_modeExplicit exit; never ends merely because a Turn ended
GoalWhole goal/change snapshot or clear tombstone, plus admitted goal-round messagesarmed / disarmed activation and driver reservationGoalService mutations; optional round drivercomplete, blocked, paused, or clear; an active goal may also be merely disarmed
TodoThe complete list in each todo/write snapshotNo separate scheduling stateThe calling agent's todo_writeEvents remain forever; the UI projection becomes null at the next turn/start

All three gain replayability from SessionEvent, but “durable event” does not mean “equally long-lived presentation.” Plan and Goal fold current state across Turns. Todo events are durable too, while the product projection deliberately interprets the list as standing only until the next Turn.

2. Plan Has Two States: Logged Mode and Pending Selection

plan/mode is a log-only, non-Surface, last-write-wins boolean event; the default without one is false. A committed value survives Session resume, Fork, and full-log replay. The controller also owns a Session-keyed WeakMap containing the target mode that has not reached its commit boundary and whether the switch should be narrated to the model. PLANSTATE-PLAN-STATE

1

Idle selection

Append plan/mode immediately when no Turn is open because no in-Turn pre-Step remains to wait for.

2

Running selection

Store only a pending intent; system-prompt assembly already reads that target value.

3

Accepted boundary

The pre-Step listener first awaits downstream policy and appends only for a non-rejected, non-aborted proposed Step.

4

Switch narration

Add one plugin user notice only when the latest request header told the model the opposite mode.

Failure semantics

A boundary append failure warns, allows the current Step to continue, and retains the pending intent for a later boundary. Same-Step Provider recovery reuses frozen assembly and does not consume the selection. Real-loop tests require the mode to take effect only in a later Step while keeping tool schemas identical. PLANSTATE-PLAN-BOUNDARYPLANSTATE-PLAN-LOOP-TEST

3. Plan Changes Guidance, Not Permission or the Tool Catalog

While active, plan:policy renders the deployment-provided section at system-prompt order 50; while inactive, it contributes empty text. The shipped policy asks the model to explore first, restrict itself to non-mutating investigation, submit a decision-complete plan, and explicitly avoid using Todo as a substitute for the reviewed plan. PLANSTATE-PLAN-ASSEMBLYPLANSTATE-SHIPPED-PLAN-POLICY

Soft policy, not enforcement

exit_plan_mode remains registered in both modes, and Plan does not filter other tools. An integration test even lets a write tool execute successfully while Plan is active. Actual restrictions depend on model compliance plus independent sandbox, approval, and tool policy. Plan mode neither reads nor writes those permission states. PLANSTATE-PLAN-LOOP-TEST

This keeps the tool schema and Code Mode SDK prefix stable, but an application cannot interpret a lit Plan chip as proof that mutation is rejected at execution. A deployment needing hard read-only behavior must configure that separately.

4. Three Plan Control Surfaces: Command Entry, Tool Review, and Direct Service

EntryBehaviorModel-history effectAuthority or failure boundary
/planSelect activeThe command line stays outside SurfaceRuns through the generic command plane; logs run/done
/plan <message>Select active, then submit an ordinary user message through agent.steer()Only the suffix message enters model historySelection happens first; a later steering failure does not roll it back
/plan offLeave, or cancel an uncommitted entryNo direct model input; a switch notice may be added when neededOnly exact lowercase off is the control word
exit_plan_mode(plan)Submit complete Markdown for human reviewCall and result remain ordinary tool historyCommitted mode must be active and review must explicitly approve
ctx.planMode.set()A trusted plugin directly selects a targetInfluences the model only through later policy or noticeIn-process service, not a public user protocol

The exit tool requires trimmed input beginning with a level-one # heading. Consent requires exactly one plan-review answer, exactly one selected Approve, and no custom text. Keep planning, free-form feedback, duplicate answers, and missing answers all become failed tool results so the model revises. Approval records only a silent pending exit: the current assistant tool batch remains under Plan, and the next accepted pre-Step commits the switch. PLANSTATE-PLAN-CONTROLS

Human ownership

The user-question service requires a supplied Agent to be the exact live registry instance and a current runtime root. A child owned by another live Agent cannot open review. Durable Fork lineage does not permanently remove authority: a Session later resumed as an independent root may review normally. PLANSTATE-PLAN-REVIEW-AUTH

5. Plan’s Web View, Projection, and Crash Recovery Are Not Equivalent

The plan projection folds both command/run(name="plan") and plan/mode. The command derives a wanted target; the mode event commits active and clears wanted. It can therefore recover { active, pending } from a cold log and distribute it through history baselines and session/projection frames to multiple browsers. PLANSTATE-PLAN-PROJECTION

The Web renders a Plan × chip only while the effective target is Plan; clicking it executes /plan off through the command Remote. The same projection selects the composer placeholder. A dedicated review panel presents Approve, Keep planning, and discuss/dismiss; providers that do not understand the intent can still render generic options. PLANSTATE-PLAN-UIPLANSTATE-PLAN-REVIEW-UI

There is no independent plan.get/plan.set business API family. The Web uses generic command Remote plus projection, the model uses the stable exit tool, and host plugins use the service.

6. Goal Is a Complete State Machine with Compare-and-Set Revisions

A Goal identity remains stable across revisions, while GoalRef { id, revision } identifies one exact version. Every snapshot carries objective, phase, and maxGoalRounds; the blocked phase additionally—and only—carries { code, message }. The four phases are active, paused, blocked, and complete. PLANSTATE-GOAL-TYPES

create → active(r1)
active → edit → active(r+1)
active → pause → paused(r+1)
active → block → blocked(r+1)
active|paused|blocked → complete(r+1)
active|paused|blocked --resume, capacity remains→ active(r+1)
any current → clear tombstone(r+1)
complete → create a fresh id at r1
Revision is not a round

Create, edit, pause, resume, complete, block, and clear advance revision. An admitted positive goal-round user/message increments only roundsStarted, not revision. The next goal mutation copies the current round count into its new whole snapshot. The strict fold rejects revision gaps, illegal phase transitions, reused goal ids, regressing timestamps, and non-sequential rounds. PLANSTATE-GOAL-FOLD

7. Durable Phase and Process-Local Activation Are Separate Axes

CombinationMeaningAutomatic continuation
active + armedThe objective may continue and this process holds automatic authorityThe optional driver may schedule after idle
active + disarmedThe durable objective is unfinished but automatic authority has been removedNo; explicit resume is required
paused/blocked/complete + disarmedThe durable lifecycle is stoppedNo

Activation is never logged. A fresh cache, every agent/session-start, driver hot-load, and uncertain failure disarm it; resume writes a new revision and arms it. Resume, Fork, and process restart can therefore restore objective, phase, revision, and round count without silently restoring autonomous execution authority. PLANSTATE-GOAL-SERVICEPLANSTATE-GOAL-COMMIT

GoalService also requires the exact live object in AgentRegistry, not merely the same id. After creation, edit, pause, resume, complete, block, and clear all compare the current GoalRef. When browser tabs, model tools, and human commands race, the first commit advances revision and later stale refs fail explicitly instead of overwriting it.

8. Goal Continuation Driver: Same Session, One Reservation, Per-Round Durability

goal/change arms or revises goal
→ driver coalesces wakeups per exact Agent
→ wait until Agent idle and no competing queued human work
→ flush pending durable prefix
→ reserve {goalId, revision, roundsStarted + 1}
→ Agent.followup(<goal_round>...)
→ pre-step validates full message + live revision before and after downstream hooks
→ admitted user/message increments roundsStarted
→ whole Agent returns idle
→ flush the settled round before reserving the next one

Each Agent's driver state permits one queued, claimed, or admitted attempt. The prompt writes the JSON-quoted objective, round/maxGoalRounds, and instructions to verify progress against current workspace, tool results, and durable state as an ordinary user message. It neither creates a new Agent nor copies the Session. PLANSTATE-GOAL-DRIVER

Competing input wins

Ordinary human work arriving before a reservation or joining its batch marks competition and makes the automatic round yield. If objective or revision changes after claim, pre-Step treats the old round as stale, preserves other claimed messages, and reschedules only after the new state settles without consuming the round number. PLANSTATE-GOAL-DRIVER

The round cap counts admitted rounds, not tokens, cost, or time. At capacity the driver writes durable blocked with code round-limit. Test source requires exactly rounds 1 through N and no extra dispatch. PLANSTATE-GOAL-DRIVER-TEST

9. Goal Tool Authority Is Stricter Than GoalService

OperationModel-tool authorityAdditional rule
get_goalExact live, running, current initiator, open TurnNo human message required
create_goalA host-attested user message in the runtime root's current TurnThe model judges whether the task is sufficiently long-running
edit / pause / resumeThe same direct-human root TurnMust read and copy the exact id/revision first
complete / blockedDirect human or the exact admitted round of the current GoalAutonomous blocked also needs the configured round minimum

Tool authority is derived from actual committed user/message.source inside the open Turn, not from model arguments. A goal round must match the current id, revision, and roundsStarted; an autonomous round may not edit, pause, or resume. PLANSTATE-GOAL-AUTHORITYPLANSTATE-GOAL-TOOLS

One final Step after terminal state

After an autonomous round successfully marks complete or blocked, current code does not call concludeTurn(). It uses deferContext() to add a <goal_complete> or <goal_blocked> closing instruction, asking the model for one user-facing message with no more tool calls. A direct-human mutation adds no such context. Tests explicitly require concludesTurn to be undefined. PLANSTATE-GOAL-TOOLSPLANSTATE-GOAL-WRAPUP-TEST

10. Goal Commands, Remote/API, and Web UI Expose Different Operation Sets

SurfaceOperationsHow it reads stateNot exposed
/goal commandshow, create, edit, pause, resume, clearCalls GoalService directly and renders textcomplete, block, per-command round cap
Goal Remote/APIcreate, edit, pause, resume, complete, clearCurrent value travels through the goal projection; mutations only acknowledge ref/clearedget and block
Web GoalBaredit, active→pause, paused→resume, clearuseProjection("goal")create, complete, blocked-resume button, activation
Model toolsget, create, five update actionsget_goal returns live activationclear

/goal executes directly in the command plane without opening a model Turn. The Web also projects durable command/run into a right-aligned command-input bubble. GoalBar reads the latest projected CAS ref for every click and combines a synchronous ref with React pending for single-flight behavior; after clear succeeds it locally suppresses that id until the null projection catches up. PLANSTATE-GOAL-COMMANDPLANSTATE-GOAL-COMMAND-UIPLANSTATE-GOAL-COMMAND-VIEWPLANSTATE-GOAL-UIPLANSTATE-GOAL-UI-REMOTE

The Host mutation façade reuses a live Agent or cold-resumes an ordinary Session and rejects identities still owned by subagent routing. It does not reuse the model tool's “direct human Turn” rule; it is a trusted control-plane mutation of the domain. GoalError currently maps to wire internal, with the stable domain code under details.goalCode, so clients must inspect details to distinguish stale revision and other business failures. PLANSTATE-GOAL-APIPLANSTATE-GOAL-API-ROUTINGPLANSTATE-GOAL-API-ERRORS

11. Goal Failure, Cancellation, and Concurrency Recovery

ConditionState consequenceAutomatic retry?
Durability checkpoint failure for a Goal change or prior roundRetain the phase in the current Session log; disarm activationNo; human resume required
Agent.followup queue failureIf revision still matches, block as queue-failedNo
Downstream pre-Step rejects a valid reservationBlock as prompt-rejected; no admitted round consumedNo
Provider/Agent error or max tokensGoal may remain active but activation becomes disarmedThe driver performs no abnormal auto-retry
Cancellation belongs to a queued/claimed/admitted goal attemptIdle checkpoint tries durable pause; falls back to disarmNo
Cancellation belongs only to ordinary human workDoes not invent a Goal phase; may only disarmNo
Plugin teardownClose admission, disarm, cancel active attempt, await quiescenceNo later round may start

The driver serializes work with one attempt and a coalesced run promise per Agent; Goal mutations serialize conflicting control planes through revision CAS. These solve different concurrency problems. If an external LLM-retry plugin recovers successfully within the same Step, the original reservation can settle; the driver itself never converts an unclassified failure into a new round. PLANSTATE-GOAL-DRIVERPLANSTATE-GOAL-DURABILITY-TEST

12. Todo Is Whole-Value Replacement, Not a Task Database

todo_write({ todos }) must send the complete list on every call. Success synchronously appends one todo/write, and current value is the last snapshot. Each item contains only normalized non-empty content and one of pending, in_progress, or completed. There is no id, priority, dependency, nesting, patch, delete-one operation, or read-back operation. An empty array is a valid explicit empty list. PLANSTATE-TODO-TOOL

Validation before write

The tool schema rejects unknown item keys and invalid enum values; execute then trims content, rejects blanks and duplicates, and enforces the deployment's active-count policy. Failure occurs before append and writes no todo/write. A non-Agent caller is rejected because it has no owning Session. PLANSTATE-TODO-TOOLPLANSTATE-TODO-TEST

allowParallelInProgress is a required deployment choice. true changes both model instructions and validation to allow several active items; false rejects more than one. The shipped base profile selects true. The durable invariant deliberately does not reapply the current active-count policy, so a log written under a permissive deployment still replays after policy is tightened. PLANSTATE-SHIPPED-TODO-POLICYPLANSTATE-TODO-INVARIANT

13. Todo Events Persist; the Standing Projection Lasts Only Until the Next Turn

Turn N: todo_write(snapshot A)
        todo_write(snapshot B)   → projection = B
        turn/end                 → projection still B
Turn N+1: turn/start             → projection = null
        ... old todo/write events remain in the Session log

The todos projection is null before the first write, replaces its whole value on every write, and clears on every turn/start. It does not clear at turn/end, so a completed checklist remains visible after Agent idle until another human or automatic goal round actually opens a Turn. Cold reads, resume, and Fork replay the same full-log fold. PLANSTATE-TODO-PROJECTION

The Web TodoPanel reads only that projection, showing a collapsible card with status counts and all items. A separate todo_write tool row derives “done/total + first active + extra-active count” from durable call arguments. The first is the product's currently standing list; the second is historical tool-call presentation. Their lifetimes differ. PLANSTATE-TODO-UIPLANSTATE-TODO-ROW

Model visibility

todo/write itself is log-only, not a second model message. The model sees its complete tool-call arguments and a compact result. A later whole-list replacement updates only the Todo projection; it does not remove earlier tool history, whose calls continue to occupy Surface until compaction or a similar mechanism shadows them. No context plugin automatically reinjects the latest Todo snapshot into later requests.

14. “Todo Allows Parallel Work” Means the Todo Tool Itself Is Exclusive

ToolRuntime classifies a call as parallel only when its definition supplies isConcurrencySafe(args) and returns exact true. Omission, exceptions, hidden tools, and invalid definitions all fail closed to exclusive. Plan exit, all three Goal tools, and todo_write do not opt in, so each is an ordering barrier. PLANSTATE-TOOL-EXCLUSIVE

Two consecutive todo_write calls in one model batch therefore execute in order, and the second deterministically replaces the first. Multiple in_progress items mean the Agent may genuinely advance work through subagents, background commands, or workflows at the same time. They are not permission for concurrent snapshot writes and do not create a shared swarm task registry. PLANSTATE-TOOL-SCHEDULER

Why Todo has no CAS

The normal production writer is one Agent's exclusive tool pipeline, where whole-value last-wins already defines order. Goal concurrently serves commands, Remote calls, model tools, and a scheduler, so revision CAS is necessary. Their concurrency models should not be mechanically unified.

15. Event, Surface, Projection, and Replay Matrix

FactSession logDirect model SurfaceCurrent browser viewResume/Fork
plan/modeYesNo; acts through the system section and optional noticeplan {active,pending}Committed active recovers; controller pending does not
goal/changeYesNo; tool result or round prompt makes state visibleWhole goal snapshotDurable state recovers; activation always disarms
Goal-round user/messageYesYesCurrent projection ignores itStrict fold recovers round count
todo/writeYesNo; call/result are visibletodos standing listRefolds latest write plus later turn/start
command/run/doneYesNoPlan pending, Goal command bubble, generic command rowPure-log reconstruction

Compaction rewrites model Surface; it does not delete these log-only domain facts. Plan active and Goal lifecycle do not depend on a summary remembering them. Todo projection also remains reconstructable from the complete log, although its next-Turn clearing rule still applies. Goal-round prompts, tool calls, and results are Surface content, so a later model may know their semantics only through a checkpoint after compaction.

16. Three Confirmed Documentation Drifts

Documentation claimCurrent production behaviorFinding
tool-goal README says autonomous complete/blocked calls concludeTurn()Source uses deferContext() for a final closing message; tests require no conclusion markerREADME is stale
ui-goal README says Remote mutations write agent/inbox/spliced and queue Goal contextGoalService directly appends log-only goal/change and injects no model contextREADME still describes an older architecture
command-goal README says there is no continuous status widgetThe Web now ships a projection-driven GoalBar with edit/pause/resume/clearThe limitation is obsolete

These findings compare production source and tests at the pinned commit; they are not inferences from prose alone. The core Plan and Todo READMEs broadly match their current control flow within this chapter's scope. Goal evolved across more packages and shows visible synchronization lag. PLANSTATE-DOC-GOAL-TOOLPLANSTATE-DOC-GOAL-UIPLANSTATE-DOC-GOAL-COMMAND

17. Confirmed Constraints, Gaps, and Deliberate Tradeoffs

FindingClassificationPractical effect
Plan provides guidance onlyExplicit designIndependent sandbox/approval must enforce hard restrictions
Plan's durable pending view and process intent can split after a crashRecovery constraintUI may show the target while prompts still use old committed state; reapply required
Only Web has specialized plan review and Plan chipPresentation boundaryOther providers use generic questions and commands
Goal projection does not apply admitted goal-round messagesConfirmed projection gaproundsStarted may remain at the latest mutation until another goal/change; service reads remain accurate
Goal projection omits activationExplicit designGoalBar cannot distinguish active-armed from active-disarmed and may offer pause rather than resume after recovery
GoalBar offers no resume button for a blocked GoalUI feature gapUse /goal resume or another Remote/tool path
Goal has no independent evaluator or resource budgetExplicit scopeCompletion and same-blocker semantics remain model judgment; the cap counts rounds only
Todo projection deliberately clears across TurnsProduct semanticsIt is not a long-lived task board; historical events remain
Todo has no read, patch, CAS, or shared scopeExplicit scopeThe model resends the whole list; it belongs to one calling Agent Session
Todo invariant does not reject extra item keysInvariant coverage gapThe tool schema rejects them, but the companion misses extra fields forged by a trusted direct Session writer
Exact projection-gap boundary

GoalService's strict cache fold reads goal-sourced user/message and immediately advances roundsStarted. The lightweight goal projection replaces only on goal/change, and tests pin the same-reference fast path for non-goal-change events. This is browser-projection freshness, not loss of the domain round. PLANSTATE-GOAL-FOLDPLANSTATE-GOAL-PROJECTIONPLANSTATE-GOAL-PROJECTION-TEST

Exact invariant-gap boundary

The Todo tool's JSON schema sets additionalProperties: false. Its companion reads content/status and checks normalization, uniqueness, and enum membership, but never compares own keys. Normal model input is protected; the gap concerns trusted producers that bypass the tool and append directly to Session. PLANSTATE-TODO-TOOLPLANSTATE-TODO-INVARIANT

18. Design Assessment and Verification Status

Design choiceBenefitCost
Plan uses a stable tool catalog and variable system sectionMore stable KV-cache shape and a clear review protocolHard restrictions must live on another permission axis
Goal separates durable phase from activationResume and Fork cannot accidentally restart autonomyUI needs another live channel to display actual continuation eligibility
Goal combines revision CAS with a one-attempt driverCross-surface conflicts fail closed; rounds are serial and auditableIts lifecycle and failure matrix is much more complex than Plan or Todo
Todo uses whole-value snapshotsSimple replay, no item identity, direct model semanticsLong lists repeatedly consume tokens and cannot support collaborative incremental edits
All three write log-only domain eventsCompaction cannot erase control stateReaders must keep complete log, model Surface, and UI projection distinct
Verification scope

This chapter traced PlanModeController, command/user-question/Plan UI, GoalService/strict fold/invariant, Goal tools/authority/wrap-up, the same-session round driver, Goal command/Remote/API/GoalBar, Todo tool/invariant/projection/UI, and their unit, integration, and real-loop test sources. Upstream dependencies are not installed, so this report does not claim local test execution. Every documented drift and gap was cross-checked against reachable production control flow at the pinned commit.

My Learning Notes

Autosaved only in this browser. Nothing is uploaded or committed. Export Markdown whenever you want to keep a copy.