Skip to content

chore: finish retiring the Task Ledger domain #4399

Description

@Astro-Han

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.0v0.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.

  1. 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.

  2. Delete the storage islandtask-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.

  3. 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.

  4. 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.

  5. 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.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or requesthelp wantedExtra attention is needed

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions