Problem
The Task Ledger domain has been retired, but only its middle layer was removed. #4351 took out the protocol operation and the coordinator — packages/runtime-host/src/__tests__/session-todo-protocol.test.ts:39 now asserts task.ledger.query is absent from HOST_OPERATION_SPECS. What sat above and below that layer is still standing.
Nothing reachable from a supported entry point uses any of it, so it produces no behavior. It does produce work: every grep for Task returns it, every storage refactor has to keep it compiling, and every new reader has to determine that a 970-line module and a 1,074-line storage cluster are dead before they can ignore them.
What is still there
packages/core/src/task-ledger.ts — 970 lines, 56 exports. Exactly three are live: isTaskLedgerEvent, projectTaskLedgerEvents, and type TaskLedgerEvent, all imported by packages/storage/src/session-todo-store.ts:22-26. The other 53 have no production consumer.
packages/storage/src/task-ledger-{store,store-internal,authority}.ts — 1,074 lines forming a closed island. They import each other and @maka/core/task-ledger; nothing else in src/ imports them, and packages/storage/package.json does not export them. Their only remaining callers are their own tests plus packages/storage/src/__tests__/{session-todo-store,sqlite-workflow-store}.test.ts.
bootstrapLegacyTasks in packages/storage/src/session-todo-store.ts:222, reached from readOrBootstrap (:98) and bootstrapAndInsert (:175) — it replays legacy Task events into a Todo document on first read of every Session.
workflow_task_ledger_events in packages/storage/src/sqlite-workflow-schema.ts:48, still created on every migration.
apps/desktop/stories/session-workbar.stories.tsx — builds Task[] fixtures (:155-159) only to map them into { content, status } at :841, i.e. it constructs the retired model in order to immediately discard it.
- Test-only importers of
type Task in packages/runtime-host/src/__tests__/ (four files plus fixtures/execution-host-suite.ts).
The last live thread, and the decision it needed. bootstrapLegacyTasks migrates rows out of workflow_task_ledger_events. The only INSERT into that table on main is packages/storage/src/task-ledger-store.ts:695, inside the dead island with no caller — so no build from main can write a row. But that is the wrong test for a compatibility path: its producer is the released build, not current main. v0.1.0–v0.1.11 and v0.2.0-incubating-rc1 all shipped a live Task Ledger, and none of them shipped bootstrapLegacyTasks or workflow_session_todo_documents — so the bridge has never run, and released users' Tasks are still sitting in that table waiting for it.
That makes the bridge a live obligation, not residue, and turns the removal into a product decision rather than a cleanup. Decided: accept the loss. Upgrading from v0.2.0-incubating-rc1 or earlier discards unfinished Tasks; SessionTodo starts empty. The affected surface is one RC and a 0.1.x line, completed and cancelled Tasks were never going to be imported anyway, and keeping an unshipped bridge alive for a pre-1.0 surface costs more than it returns. The break must be stated in the commit, the PR, and the release notes.
Desired outcome
The Task Ledger domain is gone from the repository. grep -ri "task.ledger" returns the retired-projection drop statement and nothing else. packages/core no longer ships a ./task-ledger entry point, packages/storage has no Task Ledger island, and the Todo store reads only its own table.
Net: roughly 2,000 production lines, one package entry point, one database table, one startup migration path, and 56 exported symbols disappear. Nothing moves except the three symbols named below.
Suggested approach
The order matters — take the leaves before the trunk, so each step typechecks on its own.
-
Rehome the three live symbols. isTaskLedgerEvent, projectTaskLedgerEvents, and TaskLedgerEvent exist only to serve bootstrapLegacyTasks. They go with it in step 3, so do not move them into session-todo.ts — that would relocate the domain instead of removing it.
Note the two call sites bootstrapLegacyTasks reaches are readOrBootstrap and initializeCopy; after removal neither writes anything a later read could not reconstruct, so pin the stored rows in the tests, not just the returned snapshot.
-
Delete the storage island — task-ledger-store.ts, task-ledger-store-internal.ts, task-ledger-authority.ts, and packages/storage/src/__tests__/task-ledger-authority.test.ts. Drop the island imports from session-todo-store.test.ts and sqlite-workflow-store.test.ts; the assertions there that are actually about Task Ledger go with the code, and the ones about Todo or workflow-store behavior get rewritten to set up through the Todo store's own API.
-
Delete bootstrapLegacyTasks and its two call sites. readOrBootstrap returns an empty document when no stored document exists; bootstrapAndInsert collapses into insertDocument(emptyDocument()) or disappears. The DELETE FROM workflow_task_ledger_events in the purge path (:161) goes with it. Then delete packages/core/src/task-ledger.ts and its ./task-ledger entry in packages/core/package.json.
-
Drop the table. Add DROP TABLE IF EXISTS workflow_task_ledger_events; to migrateSqliteWorkflowDatabase and bump SQLITE_WORKFLOW_SCHEMA_VERSION. The file already does exactly this for workflow_plan_reminders a few lines up — follow that, not retireReleasedWorkflowProjections, which carries a shape assertion for tables a released build created.
-
Fixtures last. In session-workbar.stories.tsx, declare the fixture as SessionTodoItem[] and delete the task() helper and the mapping at :841 — the story is already about Todo items, not Tasks. Replace the type Task imports in the runtime-host tests with whatever those tests actually assert on.
Validation: packages/storage and packages/runtime-host workspace tests, plus npm run format. A migration check is worth doing by hand — open a database created before the change and confirm the migration drops the table without error.
Alternatives or workarounds
- Keep
bootstrapLegacyTasks alone and delete the rest. Rejected: it is the only thing keeping three exports of a 970-line module alive, and it can never find a row to migrate. Keeping it preserves the whole core module for a code path that cannot execute.
- Move the three live symbols into
session-todo.ts and delete only core/task-ledger.ts. Rejected for the same reason — it renames the residue rather than removing it, and leaves a legacy replay path in the Todo store with no producer.
- Leave the table and only delete the code. Rejected: an orphan table created on every migration is exactly the kind of unexplained state that the next storage change has to reason about.
- Keep the bridge for one release cycle and defer the drop to the next schema bump. Rejected by the decision above, but it was the safe option: it costs only
bootstrapLegacyTasks, the events table, and three core symbols, and the island and the other 53 exports could still go now.
- Carry the data forward inside the schema-12 migration — a one-shot batch projection of subjects into
workflow_session_todo_documents, then drop. Rejected: it re-implements event-projection semantics in the schema file, which is where a mistake is least recoverable, to preserve a surface one RC old.
Additional context
apps/desktop/src/renderer/styles/task-ledger.css is residue from the same retirement, but it is a separate slice with its own reviewer — see #4394. That issue's scope should not grow to cover this one, and the class names it keeps (.maka-task-ledger-panel and friends, still used by packages/ui/src/session-todo-panel.tsx) are a naming question, not part of this removal.
Two mechanisms that mention Task Ledger must stay. The workflow_task_ledger entry in RELEASED_CUTOVER_STORE_VALIDATION_KEYS (packages/storage/src/operational-state-store.ts:137) pins what a released cutover writer emitted: the comment above it states that a completed journal row whose store name is absent there is evidence this build never wrote, so retirement fails closed and preserves it. Deleting the entry would block startup on exactly the workspaces it protects.
Likewise not part of this: retireReleasedWorkflowProjections and RELEASED_WORKFLOW_PROJECTION_TABLES in sqlite-workflow-schema.ts. That mechanism also serves workflow_plan_projections and encodes a deliberate shape check before dropping; it is not Task Ledger residue.
Also unrelated to #4395, which covers a duplicated grouping authority in operation-dispatcher.ts. The two share only the fact that both were found during the same review of #3781.
Found by a simplification audit run while reviewing #3781. Analysis produced with Claude Code and verified against main at c76fbda.
Problem
The Task Ledger domain has been retired, but only its middle layer was removed. #4351 took out the protocol operation and the coordinator —
packages/runtime-host/src/__tests__/session-todo-protocol.test.ts:39now assertstask.ledger.queryis absent fromHOST_OPERATION_SPECS. What sat above and below that layer is still standing.Nothing reachable from a supported entry point uses any of it, so it produces no behavior. It does produce work: every
grepforTaskreturns it, every storage refactor has to keep it compiling, and every new reader has to determine that a 970-line module and a 1,074-line storage cluster are dead before they can ignore them.What is still there
packages/core/src/task-ledger.ts— 970 lines, 56 exports. Exactly three are live:isTaskLedgerEvent,projectTaskLedgerEvents, andtype TaskLedgerEvent, all imported bypackages/storage/src/session-todo-store.ts:22-26. The other 53 have no production consumer.packages/storage/src/task-ledger-{store,store-internal,authority}.ts— 1,074 lines forming a closed island. They import each other and@maka/core/task-ledger; nothing else insrc/imports them, andpackages/storage/package.jsondoes not export them. Their only remaining callers are their own tests pluspackages/storage/src/__tests__/{session-todo-store,sqlite-workflow-store}.test.ts.bootstrapLegacyTasksinpackages/storage/src/session-todo-store.ts:222, reached fromreadOrBootstrap(:98) andbootstrapAndInsert(:175) — it replays legacy Task events into a Todo document on first read of every Session.workflow_task_ledger_eventsinpackages/storage/src/sqlite-workflow-schema.ts:48, still created on every migration.apps/desktop/stories/session-workbar.stories.tsx— buildsTask[]fixtures (:155-159) only to map them into{ content, status }at:841, i.e. it constructs the retired model in order to immediately discard it.type Taskinpackages/runtime-host/src/__tests__/(four files plusfixtures/execution-host-suite.ts).The last live thread, and the decision it needed.
bootstrapLegacyTasksmigrates rows out ofworkflow_task_ledger_events. The onlyINSERTinto that table onmainispackages/storage/src/task-ledger-store.ts:695, inside the dead island with no caller — so no build frommaincan write a row. But that is the wrong test for a compatibility path: its producer is the released build, not currentmain.v0.1.0–v0.1.11andv0.2.0-incubating-rc1all shipped a live Task Ledger, and none of them shippedbootstrapLegacyTasksorworkflow_session_todo_documents— so the bridge has never run, and released users' Tasks are still sitting in that table waiting for it.That makes the bridge a live obligation, not residue, and turns the removal into a product decision rather than a cleanup. Decided: accept the loss. Upgrading from
v0.2.0-incubating-rc1or earlier discards unfinished Tasks; SessionTodo starts empty. The affected surface is one RC and a 0.1.x line, completed and cancelled Tasks were never going to be imported anyway, and keeping an unshipped bridge alive for a pre-1.0 surface costs more than it returns. The break must be stated in the commit, the PR, and the release notes.Desired outcome
The Task Ledger domain is gone from the repository.
grep -ri "task.ledger"returns the retired-projection drop statement and nothing else.packages/coreno longer ships a./task-ledgerentry point,packages/storagehas no Task Ledger island, and the Todo store reads only its own table.Net: roughly 2,000 production lines, one package entry point, one database table, one startup migration path, and 56 exported symbols disappear. Nothing moves except the three symbols named below.
Suggested approach
The order matters — take the leaves before the trunk, so each step typechecks on its own.
Rehome the three live symbols.
isTaskLedgerEvent,projectTaskLedgerEvents, andTaskLedgerEventexist only to servebootstrapLegacyTasks. They go with it in step 3, so do not move them intosession-todo.ts— that would relocate the domain instead of removing it.Note the two call sites
bootstrapLegacyTasksreaches arereadOrBootstrapandinitializeCopy; after removal neither writes anything a later read could not reconstruct, so pin the stored rows in the tests, not just the returned snapshot.Delete the storage island —
task-ledger-store.ts,task-ledger-store-internal.ts,task-ledger-authority.ts, andpackages/storage/src/__tests__/task-ledger-authority.test.ts. Drop the island imports fromsession-todo-store.test.tsandsqlite-workflow-store.test.ts; the assertions there that are actually about Task Ledger go with the code, and the ones about Todo or workflow-store behavior get rewritten to set up through the Todo store's own API.Delete
bootstrapLegacyTasksand its two call sites.readOrBootstrapreturns an empty document when no stored document exists;bootstrapAndInsertcollapses intoinsertDocument(emptyDocument())or disappears. TheDELETE FROM workflow_task_ledger_eventsin the purge path (:161) goes with it. Then deletepackages/core/src/task-ledger.tsand its./task-ledgerentry inpackages/core/package.json.Drop the table. Add
DROP TABLE IF EXISTS workflow_task_ledger_events;tomigrateSqliteWorkflowDatabaseand bumpSQLITE_WORKFLOW_SCHEMA_VERSION. The file already does exactly this forworkflow_plan_remindersa few lines up — follow that, notretireReleasedWorkflowProjections, which carries a shape assertion for tables a released build created.Fixtures last. In
session-workbar.stories.tsx, declare the fixture asSessionTodoItem[]and delete thetask()helper and the mapping at:841— the story is already about Todo items, not Tasks. Replace thetype Taskimports in theruntime-hosttests with whatever those tests actually assert on.Validation:
packages/storageandpackages/runtime-hostworkspace tests, plusnpm run format. A migration check is worth doing by hand — open a database created before the change and confirm the migration drops the table without error.Alternatives or workarounds
bootstrapLegacyTasksalone and delete the rest. Rejected: it is the only thing keeping three exports of a 970-line module alive, and it can never find a row to migrate. Keeping it preserves the wholecoremodule for a code path that cannot execute.session-todo.tsand delete onlycore/task-ledger.ts. Rejected for the same reason — it renames the residue rather than removing it, and leaves a legacy replay path in the Todo store with no producer.bootstrapLegacyTasks, the events table, and threecoresymbols, and the island and the other 53 exports could still go now.workflow_session_todo_documents, then drop. Rejected: it re-implements event-projection semantics in the schema file, which is where a mistake is least recoverable, to preserve a surface one RC old.Additional context
apps/desktop/src/renderer/styles/task-ledger.cssis residue from the same retirement, but it is a separate slice with its own reviewer — see #4394. That issue's scope should not grow to cover this one, and the class names it keeps (.maka-task-ledger-paneland friends, still used bypackages/ui/src/session-todo-panel.tsx) are a naming question, not part of this removal.Two mechanisms that mention Task Ledger must stay. The
workflow_task_ledgerentry inRELEASED_CUTOVER_STORE_VALIDATION_KEYS(packages/storage/src/operational-state-store.ts:137) pins what a released cutover writer emitted: the comment above it states that a completed journal row whose store name is absent there is evidence this build never wrote, so retirement fails closed and preserves it. Deleting the entry would block startup on exactly the workspaces it protects.Likewise not part of this:
retireReleasedWorkflowProjectionsandRELEASED_WORKFLOW_PROJECTION_TABLESinsqlite-workflow-schema.ts. That mechanism also servesworkflow_plan_projectionsand encodes a deliberate shape check before dropping; it is not Task Ledger residue.Also unrelated to #4395, which covers a duplicated grouping authority in
operation-dispatcher.ts. The two share only the fact that both were found during the same review of #3781.Found by a simplification audit run while reviewing #3781. Analysis produced with Claude Code and verified against
mainat c76fbda.