Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
terminal coalescing, stop/drain, and durable continuation admission now have one production
owner, immutable request snapshots remain enforced at AgentRun acceptance and backend dispatch,
and SessionEvent-to-RuntimeEvent conversion remains a pure mapper.
- Retired the Task Ledger domain: SessionTodo is now the sole authority for in-session work items, and the operational-state schema drops the `workflow_task_ledger_events` table on first open. **Unfinished Tasks are not migrated and are permanently deleted.** This affects workspaces last opened by `v0.1.0` through `v0.1.11`, `cli-v0.1.0-beta.1`, `v0.2.0-incubating-rc1`, or a `v0.2.0-dev` build; those releases wrote Tasks to a table that no shipped build ever bridged into SessionTodo. Before opening such a workspace with this build, finish or export the Tasks you still need, or copy the workspace's `runtime.sqlite` aside — the migration removes the only live copy, so afterwards recovery requires a backup made in advance.
- Unified context management under one Runtime-owned policy. `MAKA_CONTEXT_*` environment overrides no longer tune or disable compaction and Tool Result pruning; model-visible archive placeholders are read on demand through bounded `ArchiveRead` calls instead of eager hydration. Previously supported overrides are ignored on upgrade: if Tool Result pruning was set to `off`, pruning is re-enabled, and there is currently no supported replacement opt-out.

## 0.1.11 - 2026-08-18
Expand Down
79 changes: 17 additions & 62 deletions apps/desktop/stories/session-workbar.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import type { ArtifactRecord } from '@maka/core/artifacts';
import type { BrowserState } from '@maka/core/browser';
import type { GitReviewReadResult, GitReviewSnapshot } from '@maka/core/git-review';
import type { SessionSummary } from '@maka/core/session';
import type { Task } from '@maka/core/task-ledger';
import type { SessionTodoItem } from '@maka/core/session-todo';
import type { SessionTrace } from '@maka/core/session-trace';
import type { ContextDiagnosticsResult } from '@maka/runtime-host/protocol';
import { ToastProvider } from '@maka/ui';
Expand Down Expand Up @@ -147,57 +147,19 @@ const RICH_TERMINAL_BUFFER = [

// ---- ledgers -------------------------------------------------------------

// Mirrors the `task-ledger` e2e fixture (apps/desktop/src/main/e2e-fixture/
// scenarios-chat.ts), which builds this tree through the SQLite store; that
// store cannot run in a browser, so the shapes are restated rather than
// imported. The long subject is deliberate: it is what proves a deep indent
// still wraps instead of pushing owner and reason off the panel.
function task(input: Partial<Task> & Pick<Task, 'id' | 'key' | 'subject'>): Task {
return { status: 'pending', createdAt: NOW, updatedAt: NOW, ...input };
}

const tasks: Task[] = [
task({
id: 'task-1',
key: 'T1',
subject: '完成会话任务台账升级',
status: 'in_progress',
owner: { actor: 'main_agent', runId: 'run-task-parent' },
}),
task({
id: 'task-2',
key: 'T1.1',
subject: '验证 SQLite authority 与并发短 key 分配',
parentId: 'task-1',
status: 'completed',
completionEvidence: 'Core 与 Storage 定向测试全部通过。',
endedAt: NOW - 120_000,
updatedAt: NOW - 120_000,
}),
task({
id: 'task-3',
key: 'T1.2',
subject: '检查窄窗口下的任务树布局',
parentId: 'task-1',
status: 'blocked',
blockedReason: '等待视觉回归截图确认 990px 视口没有文字重叠。',
owner: { actor: 'child_agent', agentId: 'local-read' },
}),
task({
id: 'task-4',
key: 'T1.2.1',
subject: '核对深层缩进、超长任务描述、owner 与阻塞原因在窄窗口中仍可完整换行且不遮挡后续内容',
parentId: 'task-3',
}),
task({ id: 'task-5', key: 'T2', subject: '同步生命周期文档与边界说明' }),
task({
id: 'task-6',
key: 'T3',
subject: '验证 Goal 一次提醒门禁',
status: 'completed',
endedAt: NOW - 60_000,
updatedAt: NOW - 60_000,
}),
// The long item is deliberate: it is what proves a long subject still wraps
// instead of pushing the panel sideways.
const tasks: SessionTodoItem[] = [
{ content: '完成会话任务台账升级', status: 'in_progress' },
{ content: '验证 SQLite authority 与并发短 key 分配', status: 'completed' },
{ content: '检查窄窗口下的任务树布局', status: 'pending' },
{
content:
'核对深层缩进、超长任务描述、owner 与阻塞原因在窄窗口中仍可完整换行且不遮挡后续内容',
status: 'pending',
},
{ content: '同步生命周期文档与边界说明', status: 'pending' },
{ content: '验证 Goal 一次提醒门禁', status: 'completed' },
];

const artifacts: ArtifactRecord[] = [
Expand Down Expand Up @@ -812,7 +774,7 @@ const unsubscribe = () => () => undefined;
* varies, and everything else stays on the populated default.
*/
function bridge(options: {
tasks?: Task[];
tasks?: SessionTodoItem[];
tasksFail?: boolean;
trace?: SessionTrace;
traceNextCursor?: string;
Expand All @@ -838,13 +800,7 @@ function bridge(options: {
todo: {
read: async () => {
if (options.tasksFail) throw new Error('读取任务失败');
return (options.tasks ?? tasks).map((task) => ({
content: task.subject,
status:
task.status === 'in_progress' || task.status === 'completed'
? task.status
: 'pending' as const,
}));
return options.tasks ?? tasks;
},
subscribeChanges: unsubscribe,
},
Expand Down Expand Up @@ -1442,8 +1398,7 @@ export const TraceCompositionUnrecorded: Story = {
render: () => <Workbar tab="inspector" />,
};

// Real path: 任务工作栏 → 追踪 on a session that has not run a turn yet — the
// state the task-ledger e2e fixture opens on.
// Real path: 任务工作栏 → 追踪 on a session that has not run a turn yet.
export const TraceEmpty: Story = {
decorators: [bridge()],
render: () => <Workbar tab="inspector" />,
Expand Down
58 changes: 26 additions & 32 deletions docs/session-todo-lifecycle.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,9 @@

# SessionTodo Lifecycle

Status: **Current**. The former Session Task Ledger is **Deprecated** and is
retained only as a one-time migration input and rollback-era storage format.
Status: **Current**. The former Session Task Ledger is **Removed**: its module,
storage, and `workflow_task_ledger_events` table are gone as of workflow schema
12. SessionTodo is the only Session task surface.

This document answers one question for Runtime, Runtime Host, CLI, and Desktop
contributors: who owns a Session's current Todo list, and what must happen to
Expand Down Expand Up @@ -70,8 +71,9 @@ model todo_read / todo_write Desktop read-only panel

`todo_write` is an internal Host tool port rather than a public Client mutation
operation. Desktop reads through `session.todo.query`. A successful replacement
commits before the Host publishes a `todo` domain invalidation. Reads and lazy
bootstrap are silent because they do not change the effective current list.
commits before the Host publishes a `todo` domain invalidation. Reads and
first-read initialization are silent because they do not change the effective
current list.

The stored document is canonical product state. Before model or Desktop
display, content passes through the shared Unicode sanitization, secret
Expand All @@ -90,25 +92,13 @@ These bounds keep the complete snapshot below the Runtime Host frame budget, so
the operation needs no paging contract.

An initialized empty list is different from no SessionTodo row. That distinction
is what makes one-time migration and explicit clearing deterministic.
is what makes explicit clearing deterministic.

## One-time legacy bootstrap
## First read

The first Host read of an uninitialized Session, through either `todo_read` or
`session.todo.query`, keeps canonical `pending` and `in_progress` Tasks at their
current status. A canonical `blocked` Task is imported as `pending` with the
same subject so unfinished work remains visible for replanning. The Host then
persists the result even when it is empty. Workflow-only blocked reasons,
ownership, evidence, hierarchy, and terminal `completed`, `failed`, or
`cancelled` Tasks are not imported.

The first explicit `todo_write` never reads or merges legacy Tasks. It writes
the requested complete list directly. Once a SessionTodo row exists, no later
read consults the legacy Task Ledger again.

Malformed legacy events fail closed without creating the initialized marker.
An explicit whole-document write can recover from malformed legacy input
because it does not decode it.
`session.todo.query`, persists an empty document and returns it. The first
explicit `todo_write` writes the requested complete list directly.

## Copy and branch semantics

Expand All @@ -120,8 +110,9 @@ lifecycle, before the target Session is published:
- a historical cut, before-revision, or side conversation initializes an
explicit empty Todo document.

Initialization is one SQLite write transaction. The source is read or lazily
bootstrapped and the absent target is inserted together. Retrying an identical
Initialization is one SQLite write transaction. The source's current document
is read — an uninitialized source reads as empty, without being written — and
the absent target is inserted in the same transaction. Retrying an identical
initialization is idempotent; a different or corrupt existing target fails
closed instead of being overwritten.

Expand All @@ -132,15 +123,18 @@ the preparing Session.
## Archive, removal, backup, and rollback

- Archive retains the current Todo document.
- Remove and incomplete-copy discard purge both the Todo document and legacy
Task rows in one lifecycle operation, so a deleted Session cannot bootstrap
stale work if its identifier is observed again.
- Remove and incomplete-copy discard purge the Todo document, so a deleted
Session cannot show stale work if its identifier is observed again.
- Backup and restore preserve both non-empty and initialized-empty documents.

There is no dual write to the legacy Task Ledger. Rollback across the cutover
therefore means restoring a database backup taken before the upgrade. That
loses Todo edits made after the backup; running an old binary directly against
the upgraded live database is not a supported rollback guarantee.
Rollback across the cutover means restoring a database backup taken before the
upgrade. That loses Todo edits made after the backup; running an old binary
directly against the upgraded live database is not a supported rollback
guarantee.

Upgrading to workflow schema 12 drops `workflow_task_ledger_events` without
migrating it. A workspace last opened by v0.2.0-incubating-rc1 or earlier loses
its unfinished Tasks; they are not imported into SessionTodo.

## Surface behavior

Expand Down Expand Up @@ -171,6 +165,6 @@ prompt. The model reads it on demand with `todo_read`.
- `apps/desktop/src/main/runtime-host-client.ts`: Desktop query adapter and
display-safe projection.

The legacy Task codecs, replay, and tables remain only for bootstrap and the
bounded migration/rollback window. Their eventual deletion must not recreate a
second product surface or change this current-document contract.
The legacy Task codecs, replay, and tables are deleted. Nothing may recreate a
second product surface for Session tasks or change this current-document
contract.
1 change: 0 additions & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,6 @@
"./agent-graph-topology": "./dist/agent-graph-topology.js",
"./agent-graph-client-projection": "./dist/agent-graph-client-projection.js",
"./agent-graph-supervisor-wake": "./dist/agent-graph-supervisor-wake.js",
"./task-ledger": "./dist/task-ledger.js",
"./session-todo": "./dist/session-todo.js",
"./foreign-session": "./dist/foreign-session.js",
"./external-session": "./dist/external-session.js",
Expand Down
Loading