| # Session Task Ledger Lifecycle |
| |
| This document defines the lifecycle and persistence contract for the task ledger |
| attached to an interactive session. It is distinct from the Headless `TaskRun` |
| record: the ledger tracks model-visible work items inside a session, while a |
| `TaskRun` records a complete benchmark or automation execution. |
| |
| ## Scope |
| |
| Maka has a session-scoped task ledger with `task_create`, `task_update`, |
| `task_list`, `task_get`, `task-events.jsonl`, `tasks.json`, and turn-tail prompt |
| injection. The implementation keeps lifecycle validation, event replay, storage |
| projection, tool access, and recovery classification on one contract. |
| |
| Non-goals: |
| |
| - no workflow engine; |
| - no cron or automation scheduling; |
| - no project-management editing, dependency graph, drag-and-drop, or bulk |
| scheduling UI; |
| - no replacement for `AgentRun`, `RuntimeEvent`, filesystem, git, test, or tool |
| evidence. |
| |
| Task status is advisory control state. It must not override real filesystem, |
| git, test, verifier, scorer, or tool evidence. |
| |
| ## Identity and Hierarchy |
| |
| Every current task has two identifiers: |
| |
| - `id` is the durable UUID primary key. It is never rewritten. |
| - `key` is the session-local short reference (`T1`, `T1.1`, and deeper forms) |
| used in prompts, tools, and UI. |
| |
| Read and update operations accept either form. Keys are allocated inside the |
| per-session serialized write queue. A child stores its parent's UUID in |
| `parentId`; its short key is allocated under the parent's key. Children cannot |
| be created under terminal parents, and a parent cannot become `completed` |
| while any descendant remains non-terminal. A parent/child edge must advance the |
| short key by exactly one segment (`T1` -> `T1.1`); skipped levels such as a |
| direct `T1` -> `T1.1.1` edge invalidate the projection and fail closed. |
| |
| Old `tasks.json` snapshots and JSONL events without `key` or `endedAt` remain |
| readable. Projection assigns stable keys in first-seen creation-event order |
| (falling back to timestamps only when event order is unavailable) and derives |
| missing terminal timestamps from `updatedAt`. The first later mutation appends |
| compatibility events before the new mutation so the derived fields become |
| durable without changing UUIDs. |
| |
| ## Task Status |
| |
| Task statuses are: |
| |
| - `pending`: declared but not started. |
| - `in_progress`: actively being worked on. |
| - `blocked`: cannot continue without external input, dependency, permission, or |
| prerequisite repair. |
| - `completed`: finished with evidence. |
| - `failed`: attempted and ended unsuccessfully with a reason. |
| - `cancelled`: intentionally stopped and should not resume automatically. |
| |
| Allowed transitions: |
| |
| ```text |
| pending -> in_progress |
| pending -> cancelled |
| |
| in_progress -> blocked |
| in_progress -> completed |
| in_progress -> failed |
| in_progress -> cancelled |
| |
| blocked -> in_progress |
| blocked -> cancelled |
| blocked -> failed |
| |
| failed -> pending |
| failed -> cancelled |
| |
| completed -> in_progress only with explicitReopen: true |
| cancelled -> pending only with explicitReopen: true |
| ``` |
| |
| ## Evidence |
| |
| New updates into these states require evidence: |
| |
| - `blocked` requires `blockedReason`. |
| - `failed` requires `failureReason`. |
| - `completed` requires `completionEvidence`. |
| |
| Evidence is compact text. Later work can replace or supplement it |
| with first-class run, tool-call, artifact, verifier, or scorer references. |
| |
| Legacy completed or cancelled tasks that predate this contract may still be |
| read from `tasks.json`. New updates must satisfy the evidence rules. |
| |
| ## Resume Trust |
| |
| The source-backed type includes a conservative `resumeTrust` classifier: |
| |
| - `trusted`: durable evidence is intact. |
| - `needs_revalidation`: state may still be correct, but related external truth |
| should be checked again. |
| - `stale`: task was active when the session or run was interrupted. |
| - `repaired`: recovery logic changed the projected state. |
| - `untrusted`: ledger, references, or state are corrupt or missing. |
| |
| The type and pure classifier are source-backed. `resumeTrust` is a system |
| diagnostic and is not injected into the model-visible task ledger until recovery |
| logic owns the value. |
| |
| Recovery/read-model classification uses the conservative classifier: |
| |
| - `in_progress` tasks are `stale`. |
| - tasks with missing required evidence are `needs_revalidation`. |
| - corrupt ledgers, invalid projections, or missing references are `untrusted`. |
| - repaired projections are `repaired`. |
| |
| ## Tool Surface |
| |
| The model-facing tools are: |
| |
| - `task_create` |
| - `task_update` |
| - `task_list` |
| - `task_get` |
| |
| The four ledger tools only mutate/read local session state; they do not dispatch |
| work themselves. `task_create.tasks[].parent_id`, all task reference inputs, |
| and `agent_spawn.task_id` accept UUIDs or short keys. `task_list` supports exact |
| `status`, `include_terminal`, and `include_archived` filters; its no-argument |
| behavior remains compatible with the original full-list behavior. |
| |
| `MAKA_TASK_LEDGER_TOOLS=false` disables registration. Legacy |
| PascalCase aliases are not advertised and require an explicit compatibility |
| flag in main-process wiring. |
| |
| ### Runtime Host Authority |
| |
| The non-serving Runtime Host composition opens the interactive Task Ledger |
| writer under its Storage root owner lease. One per-Session coordinator |
| implements the Runtime `TaskLedgerStore` port and serves the read-only |
| `task.ledger.query` Client operation. Reads, mutations, claims, and child |
| outcomes invoked through that port therefore share the same Session admission |
| boundary instead of creating a second Task Ledger authority. |
| |
| Binding the port into the Host's real-model task and child-agent tool |
| composition is part of the later Hosted execution slice. This authority slice |
| establishes the port and its Client projection without claiming that |
| non-serving Host tool composition is already active. |
| |
| Client queries return the canonical, sanitized projection in item- and |
| byte-bounded pages. A content revision pins each traversal; a continuation from |
| an older projection returns `revision_changed` rather than mixing snapshots |
| across Host epochs. The authority preserves the existing `task-events.jsonl`, |
| `tasks.json`, legacy-read, and backfill behavior. Desktop keeps its embedded |
| writer until the production cutover replaces all root writers atomically. |
| |
| ## Child Agent Ownership |
| |
| `agent_spawn(task_id=...)` resolves the task in the current session and claims |
| it only after the runtime has allocated the real child turn. The claim sets the |
| task to `in_progress` and records a `child_agent` owner. Once the child settles, |
| the owner is enriched with the real run and turn references. |
| |
| A successful child does not complete the task. The parent agent must verify the |
| result and supply `completionEvidence`. A failed or cancelled child records the |
| truthful task outcome; a child waiting for permission leaves the task blocked. |
| An active task already owned by another child turn cannot be stolen. |
| |
| ## Prompt Budget and Archive |
| |
| The current-turn task tail is capped at 8,000 characters (approximately 2,000 |
| tokens). It renders short keys rather than UUIDs and prioritizes |
| `in_progress`, `pending`, and `blocked` branches. Ancestors of included active |
| tasks are retained so hierarchy remains understandable. Up to three recent |
| terminal tasks are added when budget permits. When tasks are omitted, the tail |
| reports the omitted count and points the model to `task_list` / `task_get`. |
| |
| Terminal tasks receive `endedAt`. They become logically archived after seven |
| days: storage remains append-only and no task is deleted, while prompt and UI |
| reads exclude archived terminal tasks. Explicit tool reads may opt back into |
| them. |
| |
| Secret redaction, task-ledger tag stripping, evidence validation, and exclusion |
| of `resumeTrust=untrusted` tasks apply before model-visible rendering. |
| |
| ## Goal Completion Gate |
| |
| Ordinary interactive turns never trigger an extra model call because tasks are |
| unfinished. The turn tail is advisory only. |
| |
| When an autonomous Goal is active, its external evaluator still decides first. |
| If the evaluator says achieved or impossible, that terminal decision wins. If |
| the Goal continues and pending or in-progress task keys remain, the continuation |
| text includes one task reminder per Goal id. Blocked, failed, cancelled, and |
| completed tasks do not trigger the reminder. The reminder is consumed only |
| after the final idle check and synchronous turn injection, so a concurrent user |
| turn cannot spend it without showing it. Later continuations are allowed without |
| another task-specific reminder. Every injected decision is recorded as a |
| `task_gate_decided` AgentRun event with the Goal id, decision, and task keys. |
| When iteration, no-progress, or token caps stop a Goal, the stop event records |
| the remaining actionable task keys as well. |
| |
| ## Debug and Desktop Read Model |
| |
| The model-visible task ledger remains compact and omits `resumeTrust`, including |
| both the turn-tail injection and `task_list` / `task_get` tool results. Debug, |
| export, and trace/read-model surfaces may include task summaries with |
| `resumeTrust`, reasons, evidence, and refs. |
| |
| Desktop reads the same `Task[]` projection through `tasks:list`. Store changes |
| emit a signal-only `tasks:changed` event; the renderer reloads instead of |
| merging event payloads into a second projection. Before crossing IPC, every |
| structured Task DTO is sanitized with the same secret and task-tag redaction |
| rules used by model-visible text. The chat workspace shows a full-width, |
| collapsible, read-only task band with the active hierarchy, short keys, status, |
| owner, reason/evidence summary, and three recent terminal tasks. Session |
| switches clear the old snapshot and revision guards discard late IPC responses. |
| The panel provides loading, empty, error, and retry states, but no workflow |
| editing controls. |
| |
| This interactive task ledger remains separate from: |
| |
| - Headless `TaskRun`, the durable execution envelope across Attempts; |
| - Goal state, which owns bounded autonomous continuation; |
| - Automation and plan reminders, which own scheduled execution; |
| - `AgentRun` / `RuntimeEvent`, which own actual runtime and evidence history. |