Files
tutti/docs/architecture/workspace-workflows.md
agent-radar 2ed3415fb7
Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
源码快照 @ 2026-09-01 (source-only, 剔除非源码素材)
2026-09-01 20:17:27 +08:00

632 lines
42 KiB
Markdown

# Tutti Mode Activation And Workspace Workflows
This document describes the Tutti-owned activation and workflow models used by
Tutti Mode Plan. It covers the daemon business core, the Agent CLI boundary,
AgentGUI projection, and Issue materialization.
## Ownership Boundary
Tutti Mode is a Tutti-owned host preference, not a provider-native Agent mode
or a capability granted to the Agent. Agent sessions and turns provide
association, isolation, and provenance, but they do not own the Tutti
activation, proposal, user decision, or resulting Issue. Tutti CLI
availability is a separate runtime capability and never derives from the
activation badge.
Ownership is split as follows:
- `services/tuttid/biz/tuttimodeactivation` defines the durable activation and
immutable revision vocabulary.
- `services/tuttid/service/tuttimodeactivation` owns activation transitions,
submission snapshots, and post-commit publication.
- `services/tuttid/biz/workspaceworkflow` defines the durable workflow entities
and state vocabulary.
- `services/tuttid/service/tuttimodeplan` validates Markdown revisions,
enforces transitions, projects executable work, and coordinates downstream
operations.
- `services/tuttid/biz/tuttimodeexecution` defines the durable Tutti execution
aggregate and checkpoint vocabulary.
- `services/tuttid/service/tuttimodeexecution` owns Tutti execution
materialization and later orchestration commands.
- `services/tuttid/data/workspace` stores workflow metadata in SQLite. Plan
content is kept in immutable files under the Tutti state directory.
- `services/tuttid/service/cli/providers/tuttimodeplan` exposes Agent-callable
propose, revise, and get commands. It deliberately exposes no decision
command and no wait/poll command: the agent's turn ends after propose or
revise, and the user's review decision reaches the agent as a new user
message.
- `packages/agent/gui/workspaceWorkflow` renders the daemon snapshot and sends
user decisions through the desktop runtime adapter. It contains no workflow
business state machine.
- Workspace Issue Manager owns the Issue and task graph after an accepted
task-graph revision is materialized.
Provider-native Plan mode remains independent. `/plan` may enable a provider's
planning behavior, while `/tutti` creates or advances a Tutti-owned
`TuttiModeActivation`. The badges are compatible, and removing either badge
only disables that modifier. The Tutti badge creates durable activation state,
not a workflow; a workflow begins only when the Agent invokes the Tutti CLI.
Workflow CLI capabilities remain discoverable when activation is inactive, but
their mutation paths require the user-controlled activation to be active. The
Agent has no CLI or service mutation that can change this state; it must ask the
user to turn Tutti Mode on manually. The persisted `agent_command` source is
retained only so revisions written by older builds remain readable.
## Durable And Derived Entities
| Entity | Persistence | Purpose and relations |
| --------------------------- | -------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `TuttiModeActivation` | SQLite | Tutti-owned session-associated activation root. It points to the current immutable activation revision; Agent Session exposes only an independent read projection. |
| `ActivationRevision` | SQLite | Append-only active or inactive transition. A submitted Turn uses one exact revision so later badge changes cannot rewrite running or historical work. |
| `WorkspaceAgentSubmitClaim` | SQLite | Generic idempotency record from `clientSubmitId` to one preallocated canonical Turn ID. A prepared claim is retained when delivery is ambiguous, so a retry reconciles or reports unknown instead of dispatching again. |
| `TuttiModeTurnSnapshot` | SQLite | First-write-wins link from one canonical Turn to the exact activation revision observed before provider dispatch. Same-Turn guidance reuses it instead of reading current state. |
| `WorkspaceWorkflow` | SQLite | Tutti-owned lifecycle root. It belongs to one workspace, records its source Agent session and any trusted turn/tool-call provenance available at creation, and points to the current revision. |
| `TuttiModePlan` | SQLite | Type-specific one-to-one marker for a `WorkspaceWorkflow`. It does not contain plan text. |
| `PlanRevision` | SQLite metadata plus immutable Markdown file | Append-only version of the proposal. Metadata records sequence, schema, path, SHA-256, and producing turn. |
| `WorkflowCheckpoint` | SQLite | A user-owned decision gate bound to exactly one revision. The task review is the only active checkpoint kind; the configuration-review kind survives solely as legacy vocabulary on retired workflows. |
| `WorkflowTurnLink` | SQLite | Optional provenance link from a workflow to source, decomposition, revision, or feedback turns. |
| `WorkflowOperation` | SQLite | Idempotent record of a downstream side effect such as generating a task graph, creating a revision, or creating an Issue. A successful Issue operation stores the Issue ID. |
| `WorkflowMutation` | SQLite | Durable caller mutation ledger for propose/revise response-loss recovery. Its scoped request ID is the identity; the input SHA-256 detects conflicting reuse, and the row points to the committed workflow/revision/checkpoint result. |
| `ActionableItem` | Derived only | Read-only projection of a task from the accepted current task-graph revision. It is never a second task store. |
| Workspace Issue and Task | SQLite in Issue Manager | Reusable Issue graph materialized from accepted `ActionableItem`s. It links back through `sourceSessionId`; the workflow operation links forward through `issueId`. |
| Tutti execution | SQLite | Tutti-owned orchestration authority for one accepted workflow and Issue. Initial status is `awaiting_schedule` at graph revision 1. |
| Execution checkpoint | SQLite | Ordered durable orchestration gate. Materialization creates one active `initial_schedule` checkpoint and no Issue Run; every terminal Run later appends one deterministic settlement checkpoint. |
| Execution mutation | SQLite | Source-session-scoped mutation ledger for exact checkpoint/revision graph changes. It provides replay/conflict detection and records the incremented graph revision. |
The relation is:
```text
clientSubmitId --- WorkspaceAgentSubmitClaim --- canonical Agent Turn
|
v
Agent Session 1 ----- 0..1 TuttiModeActivation
|
+--- * ActivationRevision
| |
| +--- * TuttiModeTurnSnapshot ---+
Agent Session / Turn / Tool Call (workflow provenance)
|
v
WorkspaceWorkflow 1 --- 1 TuttiModePlan
|
+--- * PlanRevision --- 1 immutable Markdown file
| |
| +--- * WorkflowCheckpoint
|
+--- * WorkflowTurnLink --- Agent Turn
|
+--- * WorkflowOperation --- 0..1 Workspace Issue
|
+--- * WorkflowMutation --- committed revision/checkpoint result
^
|
accepted current task-graph revision
-> ActionableItem projection
-> Workspace Issue + Tasks
-> Tutti execution + initial_schedule checkpoint
```
`ActionableItem` exists only when the current revision is a `task_graph`, its
bound checkpoint is accepted, and that revision is still current. Pending,
rejected, superseded, and stale revisions project no executable work.
`TuttiModeActivation` and `WorkspaceWorkflow` are independent roots. Activating
the badge does not create a workflow, and invoking the CLI without an active
badge may still create a workflow. A workflow reaches an activation revision
only indirectly through its source Turn provenance; it never changes the
badge.
The Agent CLI runtime context supplies `AgentSessionID` as the workflow
association and isolation key. `plan revise` and `plan get`
require the same caller session as the workflow's `sourceSessionId`; a mismatch
is deliberately reported as not found. This is a local capability boundary,
not a claim that an environment variable is a malicious-client authentication
credential. The daemon host is responsible for constructing the CLI runtime
context. Agent plan proposal and revision commands require the exact active
Turn ID from that context and fail closed when it is unavailable. The plan
service resolves that Turn through the activation service and verifies the
document's explicit `execution.effect` and `execution.speed` equal its frozen
snapshot before writing an immutable revision or workflow mutation. It never
substitutes the mutable current Session preferences. `sourceToolCallId` remains
empty until a trusted tool-call seam exists; App CLI `ParentCommandID` is
nested-command context and must never be recorded as an Agent tool-call ID.
The same caller authority applies after Issue materialization. Generic Issue
Manager mutations cannot modify a `tutti_mode_plan` graph; they return
`tutti_issue_managed` with `recommendedAction = open_source_session`. The
source Agent uses `tutti plan issue mutate` and supplies the active execution
checkpoint and expected graph revision. Admission validates all operations and
the resulting dependency graph in one SQLite transaction, advances the graph
revision, rebinds the checkpoint, and cancels stale prepared/dispatched Goal
Review state and reviewer wakes. Logical supersession preserves obsolete
task/Run/output history while removing that task from the active projection;
running tasks must settle before they can be superseded.
## Activation And Turn Snapshot Flow
Before a Session exists, AgentGUI keeps the `/tutti` choice as an engine-owned
pending activation intent. Session creation carries that intent explicitly;
the daemon persists the Activation before starting the initial submission and
returns the independent read projection. Existing-session badge changes use a
revision-checked, idempotent activation command.
For every initial submission or ordinary Turn, the daemon allocates the
canonical Turn ID before provider dispatch. When a `clientSubmitId` is present,
the generic submit claim durably reserves that exact ID first. The daemon then
persists a `TuttiModeTurnSnapshot` under the same ID before calling the runtime.
A persistence failure therefore cannot start provider work. The runtime must
accept and return that exact ID; it may not replace it with a runtime-generated
one. Same-Turn guidance reads the already-bound snapshot instead of observing
the current badge. A missing snapshot for an existing Turn fails closed rather
than being reinterpreted as inactive. Removing the badge appends an inactive
revision: it cannot alter an in-flight Turn, while the next new Turn receives
the inactive snapshot. An explicit inactive revision retains its activation
identity; a Session that has never been activated uses the canonical
unconfigured inactive snapshot.
Runtime acceptance has three explicit outcomes. A definite pre-dispatch
rejection abandons the prepared snapshot and submit claim. The API maps
`clientSubmitId` into typed create/send input, and the service passes it through
typed runtime input instead of recovering it from diagnostics metadata. After
`Exec` reports provider acceptance, the Agent service explicitly invokes the
required `RuntimeController.DurablyReportSubmitProvenance` method before any
snapshot or claim promotion. The runtime adapter only maps and delegates this
typed call. Because it occurs after `Exec` releases the Session lifecycle lock,
the controller can wait on an uncoalesced FIFO durability barrier: the ordinary
submitted report must already have created the canonical Turn, then one SQLite
transaction writes the enriched Session projection and stable `clientSubmitId`
user message against that exact Turn. After the barrier succeeds, the service
accepts the bound snapshot, then reads the exact message provenance back before
accepting the submit claim; retry reconciliation repeats both promotions
idempotently. A
barrier failure, or an accepted response with a different Turn ID, retains the
claim, Session, activation, and snapshot.
A retry with the same `clientSubmitId` first reconciles the preallocated Turn
only against durable per-submit provenance and never performs a second
provider dispatch while delivery remains unknown. A live runtime Turn, an
accepted activation snapshot, or a matching Turn ID without that submit
provenance is not enough evidence—especially because multiple guidance
submissions may target the same Turn. This is an intentional at-most-once
safety boundary; ambiguity is surfaced rather than guessed away. A crash after
claim reservation but before the barrier commits leaves a prepared—not
accepted—claim conservatively unknown. A crash after the barrier commits is
deterministically reconciled from the stable message without another provider
dispatch. Same-Turn guidance uses one distinct message per `clientSubmitId`, so
multiple guidance submissions remain independently recoverable without
rewriting the active Turn lifecycle.
The runtime derives a canonical Tutti-owned Host Context only from that exact
snapshot. It does not derive instructions from `capabilityRefs`, user Prompt
text, transcript markers, or arbitrary metadata. Provider adapters use the
strongest host seam they expose. Codex uses developer instructions when its
collaboration-mode contract is available; Codex without that negotiated seam
and standard ACP use a provider-transport-only synthetic prompt block; Claude
uses a coordinator-owned synthetic message. These fallbacks are transport
details, not user-authored messages: Tutti's visible user message, prompt
preview, and activity transcript remain unchanged. The context explicitly
distinguishes Tutti activation from provider Default/Plan mode and reasoning
effort. Reusing an existing provider Session does not weaken that boundary:
each new Turn receives the current immutable activation snapshot before
dispatch. The snapshot's `state` is the sole authority for reporting Tutti Mode
status; provider Default/Plan mode and workflow existence are independent facts
that cannot override it. A clear plan request still enters the Tutti proposal
workflow instead of returning a chat-only plan. An active snapshot is a
directive, not a suggestion: the Agent must
not execute the user's request directly in that turn. It first asks focused
clarifying questions when the request is ambiguous or missing key
constraints, then submits one complete tutti-mode-plan/v1 document through a
single run of the `tutti plan propose` shell command and then ends the turn
immediately — there is no wait command, and the user's review decision
always arrives as a new user message. Because providers repeatedly misread
the bare directive as a built-in tool they lack and fall back to provider
planning surfaces (`update_plan`, TodoWrite, chat-only Markdown plans), the
active context also carries one worked example per workflow step: the
resolved CLI executable name (`tutti-dev` on development installs), the
exact `plan propose` / `plan revise` command lines, and a minimal
valid plan document showing the frontmatter task graph. The guide requires a
complete launch configuration on every task — `agentTargetId`, `model`, and
`permissionModeId` copied from `agent composer-options` output, never
invented — plus explicit `execution.effect` / `execution.speed` preference
snapshots. The existing v1 `reasoningIntensity` and
`orchestrationIntensity` fields retain their provider-reasoning and
Issue-orchestration meanings; speed is never encoded into
`orchestrationIntensity`. It routes model assignment through the injected
`$tutti-model-allocation` skill so C0-C3 task requirements, the effect floor,
speed ranking, hard capabilities, and effect-scaled validation are applied
consistently. The skill ranks joint Agent/model candidates across every
plausible target: the planning Agent, current provider/model, and provider
defaults receive no affinity bonus, while an equally qualified non-planning
target wins the tie for safely independent work so the planner remains
available for coordination and final integration. Unless the user asks for
supervised execution, the guide directs agents to the permission mode whose
semantic is `full-access` (codex `full-access`, claude-code
`bypassPermissions`): the user's approval happens once at plan review, so
accepted tasks must not stall on mid-task approval prompts. The same resolved
CLI name is used by the plan-revision feedback prompt.
Read-only investigation is allowed while writing the plan; provider-native
planning modes must not substitute for the Tutti plan workflow. Activation
still does not gate tool availability—Tutti CLI capabilities remain available
whether the state is active or inactive—but executing work the user has not
accepted through plan review goes against the user's intent.
## Markdown Revision Contract
Every proposal file uses the `tutti-mode-plan/v1` schema: YAML frontmatter
followed by a non-empty Markdown body. The flow is single-shot: every revision
is one complete document containing the plan narrative (body) plus the full
task graph (`tasks` frontmatter, at least one task, acyclic). `phase` may be
omitted and defaults to `task_graph`. `phase: configuration` remains parseable
only so legacy revision files stay readable; new configuration-phase proposals
and revisions are rejected. Rejected revisions are replaced by a new complete
immutable revision.
The frontmatter owns:
- title and workspace topic ID;
- sequential or parallel execution mode — parallel mirrors the Issue Manager's
honest-parallelism gate at propose/revise ingress: every task must carry its
own unique absolute execution directory, or the document is rejected while
the agent can still fix it (never after acceptance, where the failed
`create_issue` operation would strand an accepted workflow);
- Issue-level reasoning and orchestration intensity; Tutti Host Context
compiles the explicit speed preference into a 1-4 upper parallel target while
the durable scheduler still enforces dependency, isolation, budget, and
workspace-capacity limits;
- explicit effect and speed values copied from the producing Turn's immutable
activation snapshot; both fields are required for Agent CLI proposals and
revisions, and either a missing snapshot or an unequal value rejects the
mutation before revision-file persistence;
- auto or fixed token budget and quota waterline (the token limit is dormant
and no longer surfaced in UI);
- task IDs, content, priority, assignment (agent target, model plan, model,
permission mode, reasoning effort), execution directory, and dependencies.
Files live under:
```text
<state-dir>/tutti-mode-plans/<workflow-id>/revisions/<sha256>.md
```
The globally unique workflow ID is the opaque file scope; workspace IDs never
appear in state-directory segments. Publishing uses an atomic link. If the
same content-addressed target already contains the same digest, a retry is an
idempotent success; a mismatch fails closed. Reads verify the stored SHA-256
before parsing. Multiple intentional revisions may reference the same
content-addressed file; revision ID and per-workflow sequence remain unique,
while document path is not revision identity. The OpenAPI response includes
parsed document data for clients; renderers do not read the state directory
directly.
## State And Decision Flow
```text
Agent: tutti plan propose --file <plan.md> --request-id <stable-id>
(plan.md = complete narrative + full task graph in one document)
-> daemon validates effect/speed against the exact producing Turn snapshot
-> daemon commits workflow + revision + the single pending task review
-> daemon publishes workspace.workflow.updated
-> AgentGUI pulls the authoritative session-scoped pending snapshot
-> Agent's turn ends with the propose response (nextAction "stop")
-> user accepts (optionally with per-task assignment overrides),
rejects with feedback, or cancels through HTTP; turning Tutti mode
off with a review still pending cancels the checkpoint the same way
-> the decision reaches the Agent as a new user message when follow-up
work is needed; there is no agent-side wait
review rejected ("request changes")
-> the rejection commits durably with its create_revision operation
-> the daemon dispatches a feedback turn to the source Agent session
containing the feedback and revise instructions, idempotent through a
checkpoint-scoped clientSubmitId and linked back as a feedback turn
(best-effort; the committed rejection never depends on dispatch)
-> Agent appends a complete replacement plan with tutti plan revise
-> daemon validates the replacement against that revision Turn's snapshot
-> revision append atomically completes that exact pending operation
-> the panel refreshes onto the new pending review
review accepted
-> the decision durably records any per-task assignment overrides
-> daemon derives ActionableItems (document values merged with overrides)
-> deterministic create_issue operation atomically materializes one Issue,
its tasks, one Tutti execution, and one active initial_schedule checkpoint
-> materialization creates no Run and invokes no task launcher
-> operation succeeds with issueId and CLI reports issue_created
```
Legacy two-phase workflows are retired at daemon startup: a one-shot scan
cancels every non-terminal workflow whose current pending checkpoint is a
configuration review (actor `tutti`). Historical accepted configuration
checkpoints may still advance to a task-graph revision, but new
configuration-phase revisions are rejected.
The user-facing HTTP decision endpoint is the sole approval boundary. Agent
CLI commands can observe a decision but cannot approve their own proposal.
Rejection requires feedback. Repeating the same decision is idempotent;
attempting a different decision or deciding a stale checkpoint fails closed.
The decision compare-and-set includes the current workflow revision and status,
so a concurrent revision cannot be overwritten by a late decision. Revision
append likewise compares source session, current revision, workflow status,
checkpoint identity, and checkpoint status in its final SQLite transaction.
This prevents either side of a revise/decide race from reviving stale state.
Proposal and revision writes have a separate durable mutation ledger. Its
unique key is `(workspace, source session, mutation kind, workflow scope,
request ID)`: proposal scope is empty, and revision scope is the workflow ID.
The caller must reuse the request ID when retrying the same mutation after a
timeout or lost response. The same key and Markdown SHA-256 returns the
original workflow/revision/checkpoint result without another mutation; the same
key with different bytes fails as a conflict. A new request ID is an explicit
new mutation even when the Markdown bytes are identical, so content hashes are
never treated as user intent or idempotency identity. The CLI reports both
`requestId` and `replayed`.
Operations use deterministic identities derived from workflow, checkpoint,
and operation kind. The Issue ID is also deterministic for the workflow. Its
reserved namespace and identity constructor live in the daemon-owned
`workspaceworkflow` business model; the reusable workspace Issue package only
validates generic Issue semantics and does not know Tutti workflow identity.
This allows retries to converge on the existing operation or Issue
instead of duplicating a side effect. A failed `create_issue` operation is
requeued through a compare-and-set transition to the same pending operation
when `decide` is replayed. The retry clears stale error
and completion fields; the idempotent Issue materializer then converges on the
same Issue ID.
For a new decision, the checkpoint, resulting workflow status, and deterministic
operation are one SQLite transaction. If operation insertion fails, the entire
decision rolls back. Once that transaction commits, downstream materialization
failure does not turn an accepted checkpoint into an apparent approval error:
the response carries the committed checkpoint and durable failed operation.
Only failure to record the operation outcome is returned as an execution error.
If a decision response is lost, replaying the same decision finds the same
checkpoint and deterministic operation; a succeeded operation is not executed
again. During daemon composition, after the workflow and Issue services exist
but before the public daemon service graph is returned, a one-shot recovery
scan resumes accepted task graphs with pending or failed `create_issue`
operations against the same deterministic Issue identity. Successful rows
leave the recovery set, and a later `plan wait` or decision replay remains a
secondary idempotent repair path. Revision read/parse and other operation-local
failures are recorded on that operation and do not block recovery of later
workflows; startup fails only when the scan fails or a durable operation outcome
cannot be written. No background workflow worker is started.
The workflow service owns the transition policy. The single active checkpoint
kind is the task review; the configuration-review row below is legacy-only
(retired at startup, cancel remains valid):
| Checkpoint | Decision | Durable result | Agent next action |
| ----------------------------- | -------- | --------------------------------------------------------------------------------- | ------------------------------------------------ |
| Task review | Accept | workflow becomes accepted; overrides recorded; `create_issue` operation | `issue_created` after successful materialization |
| Task review | Reject | workflow remains in progress; feedback stored; feedback turn dispatched to source | revise the complete plan |
| Task review | Cancel | workflow becomes canceled | stop |
| Configuration review (legacy) | Cancel | workflow becomes canceled | stop |
Source Agent Session deletion is guarded at the Agent Host boundary against the
entire canonical deletion closure. Before Host closes a runtime or mutates
persistence, the daemon records one durable deletion admission for that exact
closure. Any execution whose source Session is in the closure and whose status
is not `completed` or `archived` rejects the whole request with
`tutti_execution_active` plus the complete protected Issue set. This applies
equally to single deletion, batch deletion, and workspace clear. Materialization
checks the same durable admission in its creation transaction, so a new
execution cannot race an admitted source deletion.
After admission, Host owns runtime closure and delegates canonical Session,
activation snapshot, and Turn deletion to the daemon store in one SQLite
transaction. Host reports terminal success or failure so the admission becomes
`finalized` or `superseded`; a replan must pass admission again. At daemon
startup, admissions left `admitted` by a previous process are superseded before
Host serves requests. This keeps the source deletion guard on the provider-
neutral lifecycle path instead of pre-closing runtimes in an adapter.
Stopping Tutti Mode is an archive saga, not source Session deletion. The archive
request atomically changes the execution to `archiving`, pauses Issue dispatch,
cancels pending checkpoints, wakes, reviewer work, and prepared launches, and
records a durable idempotent operation with actor and reason. The service then
cancels every running Issue Run through the exact-run coordinator and waits for
authoritative settlement. Only after no Run remains `running` may the execution
become `archived` with `archived_at`, `archived_by`, and `archive_reason`.
Cancellation failure leaves a durable failed operation and the execution fenced;
startup recovery retries each incomplete archive independently. There is no
force-archive path.
## HTTP, Events, And Recovery
The schema-first HTTP surface is:
- `GET /v1/workspaces/{workspaceID}/agent-sessions/{agentSessionID}/tutti-mode-activation`
returns the independent activation projection, including null before the
first activation;
- `PUT /v1/workspaces/{workspaceID}/agent-sessions/{agentSessionID}/tutti-mode-activation`
appends an idempotent, revision-checked active or inactive revision;
- `GET /v1/workspaces/{workspaceID}/workflows?sourceSessionId=...` lists every
workflow for one Agent Session; adding `checkpointStatus=pending` narrows the
result to workflows with a pending checkpoint. The hand-written client
exposes these as `listWorkspaceWorkflows` and
`listPendingWorkspaceWorkflows` respectively;
- `GET /v1/workspaces/{workspaceID}/workflows/{workflowID}` returns one full
authoritative snapshot;
- `POST /v1/workspaces/{workspaceID}/workflows/{workflowID}/checkpoints/{checkpointID}/decision`
records a user decision.
- `POST /v1/workspaces/{workspaceID}/tutti-executions/{issueID}/cancel-execution`
pauses dispatch and cancels active Runs for a proven Tutti-owned Issue
without archiving the execution;
- `POST /v1/workspaces/{workspaceID}/tutti-executions/{issueID}/archive`
starts or replays the durable archive saga;
- `GET /v1/workspaces/{workspaceID}/tutti-executions/{issueID}/archive?operationId=...`
returns its authoritative operation state.
`workspace.tuttimode.updated` and `workspace.workflow.updated` are advisory
invalidation events. The former identifies the changed Session and activation
revision; the latter carries workflow, source Session, checkpoint, and change
kind. Clients re-pull their authoritative HTTP or Session projection. Event
loss is safe because committed SQLite and revision-file state remains
authoritative. Every event-stream `connected` transition, including automatic
reconnect, invalidates every currently subscribed workspace scope. AgentGUI
then lists pending workflows for the active workspace and source Session again;
the new request sequence invalidates any list result that began before the
connection transition. Activation independently comes from the Session
projection. The desktop adapter never opens a panel from transcript text or a
model-authored marker. Renderer state carries the same workspace and
source-session scope key. A scope change immediately hides the previous
snapshot and invalidates in-flight list or decision results before they can
mutate the newly selected Session.
Every daemon service response containing an Agent Session passes through one
canonical response projection seam. That seam joins persisted Protocol v2 Turn
state and the independent `TuttiModeActivation` read projection for create,
resume/get, settings, title, visibility, pin, and list paths. A mutation must
not return a hand-built partial Session, because an authoritative client upsert
would otherwise interpret an omitted activation as removal and clear the badge.
The GUI panel displays only the current revision and its single pending task
review. Before acceptance it exposes every Issue materialization input in that
revision: execution mode, reasoning and orchestration intensity, quota
waterline (the token limit is dormant and hidden), plus each task's ID,
content, priority, execution directory, and dependency IDs. Each task's
assignment (Agent target, model plan, model, permission mode, reasoning
effort) is editable in place through host-supplied option catalogs; the edits
travel with the accept decision as per-task overrides and are durably recorded
on the checkpoint. Accept, reject, and cancel post the decision to the daemon;
rejection feedback is mandatory. Closing the app does not discard the review:
the next mount reconstructs it from the daemon snapshot.
Desktop assignment catalogs load provider composer options through the same
activity-core target-scoped cache used by the main Composer. The workflow
adapter may join that canonical snapshot with the workspace Agent directory and
compatible model-plan catalog, but it does not create a second provider-options
transport path. Model and reasoning entries retain their canonical
`value`/`label` metadata end to end: task overrides persist the opaque provider
`value`, while editable and read-only review surfaces render the `label` and
fall back to the value only for an unknown historical selection.
The Tutti activation additionally carries a session-scoped orchestration
intensity (0-100, default 50). The composer's Tutti Budget popup persists it
as a new activation revision; each turn's frozen snapshot copies it, and the
Tutti Host Context exposes it so the planning Agent chooses decomposition
granularity from it.
## Issue Projection
Accepting a current task graph synchronously asks the daemon-owned Issue
materializer to create an Issue from the derived `ActionableItem`s. One SQLite
transaction creates the Issue, tasks, Tutti execution aggregate at
`awaiting_schedule`/graph revision 1, and its active `initial_schedule`
checkpoint. It maps the Markdown execution profile, budget, assignments,
model choices, directories, and dependency graph into the Issue Manager
contract and records `planningSource = tutti_mode_plan`.
Materialization is inert: it creates no Run and calls no task launcher. The
source Agent must later schedule an explicit task set through the checkpoint-
and-revision-fenced Tutti execution command surface. Existing `autoAccept`
values remain readable migration metadata but have no dispatch authority for
Tutti-owned executions. Manual and `traditional_plan` Issues keep the generic
Issue Manager dispatch behavior.
`plan issue schedule` derives source authority from the trusted invoke context.
While holding the Issue mutation lock, the daemon validates the entire
requested set, including isolation. One SQLite transaction revalidates the
durable caller, active checkpoint, graph revision, task/dependency state,
budget, and workspace capacity. Fixed-budget admission reserves one estimated
allowance for every same-Issue active Run before evaluating the requested set.
The transaction then creates every Run, task-running projection, and durable
launch intent while resolving that checkpoint. Any rejection leaves the
execution and Issue graph unchanged.
Agent launch happens after commit. The launch-intent row is the source of truth
for `clientSubmitId`; delivery workers claim it with a durable lease and pass
that stored value unchanged into Agent Host. The active worker renews the lease
throughout worktree creation and Agent delivery. Ordinary recovery never steals
a leased intent, and startup recovery requeues only an expired lease, so a
second daemon replica starting while the first launch is live cannot enter the
launcher. Response-loss recovery retries the same deterministic
`issue-run:<runID>` identity, allowing Host to converge on the existing
canonical Turn rather than creating a second one. Tutti settlement,
reconciliation, and cancellation resolve this persisted identity from the
launch intent; only generic Issue Runs derive the deterministic default.
The existing Issue Run reconciliation queue also requeues expired launch
ownership and recovers prepared intents on every pass. A daemon that restarts
before the previous owner's last lease expires therefore retries immediately
after that expiry instead of leaving the Run stranded until its hard timeout.
A definite launch failure is terminal only when the adapter can prove that no
canonical Turn exists. An empty create result alone is insufficient: the
adapter also checks Host by the exact persisted `clientSubmitId`. A found Turn,
a lookup error, `ErrSubmitDeliveryUnknown`, or a create error carrying a Turn ID
remains recoverable. Only a clean Host not-found permits terminalization. That
write is one owner-fenced SQLite transaction: the currently leased launch
intent, Run, Task, Issue projection, and deterministic `task_failed` checkpoint
either commit together or all roll back. An expired owner therefore cannot
settle a Run after another daemon has reclaimed its lease.
Every terminal Issue Run (`completed`, `failed`, or `canceled`) creates one
ordered deterministic checkpoint. Completion remains
`pending_acceptance`/`agent_claimed` until the source Agent reviews that
checkpoint. The Agent may resolve the checkpoint by scheduling an exact next
task set, or acknowledge it when another Run is active or a later settlement is
already queued. Acknowledge accepts a successful task, promotes the next
pending checkpoint, and never invokes the generic dispatch frontier. Failed
and canceled checkpoints preserve their terminal task projection. Settlement
repair runs at startup and on the periodic Issue reconciliation cadence, so a
crash after Run persistence cannot lose the review gate.
When every task is terminal and every terminal Run has its settlement
checkpoint, the execution appends one ordered `all_tasks_terminal` checkpoint
with `requiresGoalReview`. Promoting it moves the execution to
`pending_goal_review`; the generic acknowledge command deliberately cannot
resolve this boundary. If a graph mutation adds or reworks tasks while that
terminal checkpoint is still pending, the mutation supersedes the stale
terminal boundary. After the replacement work settles, the same deterministic
terminal checkpoint is rearmed at the current graph revision and moved behind
the new settlement checkpoint. This preserves one terminal identity while
ensuring the final settlement can always promote a current Goal Review. The
settlement recovery scan applies the same readiness check to persisted
executions whose replacement Run had already settled before this invariant was
enforced, so daemon recovery repairs the stranded terminal backlog without
weakening acknowledge fences.
After acceptance the source conversation embeds a live "issue panel view"
(board/list) of the materialized Issue, fed by the same workspace issue events
the Issue Manager consumes. The embed surfaces the durable task structure and
execution state; Tutti-owned scheduling, mutation, and completion authority
comes from the source conversation rather than generic Issue mutation or
automatic dispatch.
The embedded panel's task-level accept and rework controls are source-Agent
prompt actions. They append a localized instruction with the exact Task mention
to the source conversation draft, preserve existing draft content, and require
an explicit user send. The renderer does not translate them into generic Task
status writes. After submission, the source Agent reads canonical execution
state and uses the existing checkpoint/revision-fenced command path. Direct
daemon-owned commands such as execution cancellation remain commands rather
than prompts.
The workflow remains the review and provenance record; the Issue remains the
task/run evidence graph; and the Tutti execution is the orchestration
authority. No renderer or Agent turn recreates the graph, and no Agent is
instructed to issue a second create call. If materialization fails, the
`create_issue` operation records a durable failure code and message, and the
authoritative workflow snapshot remains inspectable through `tutti plan get`
and the HTTP detail endpoint. Startup recovery, replaying the accepted decision,
or observing it through `tutti plan wait` retries the same durable operation;
it never asks the Agent to create the Issue separately.
The Agent-facing `issue create-from-plan` command accepts only
`traditional_plan` provenance. It rejects `tutti_mode_plan`; that provenance
can be written only by the daemon workflow materializer after the matching
checkpoint is durably accepted.
## Validation Boundaries
- Change the OpenAPI source before generated HTTP clients.
- Change the event protocol definition before generated event types.
- Keep workflow transitions and Issue materialization decisions in `tuttid`.
- Keep AgentGUI as a snapshot projector and decision client.
- Do not infer workflow state from provider Plan cards, transcript ordering,
Markdown markers in messages, or the `/tutti` badge.
- Do not infer activation state from historical `capabilityRefs`; they are
submission provenance only.
- Do not persist `ActionableItem` separately from the accepted revision.
- Do not expose Agent-callable accept or reject CLI commands.