feat(loop): track loop progress in durable chat tasks, not PROGRESS.md - #1119
Merged
Merged
Conversation
An autonomous loop's plan now lives in event-sourced per-chat task state, read and written through the mcp__kanna__task_* tools, instead of a markdown tracking file. The loop /clears main on every wake, which nulls the session token and empties the CLI's own task list, so neither the native Task tools nor the client-side task card could carry a plan across iterations. Adds a chat-tasks event log folded into StoreState and the snapshot, so task state survives /clear, a server restart and the transcript tail window. Native CLI Task calls are mirrored under an n: id namespace alongside Kanna-minted k: ids; on context_cleared the native ones are marked stale rather than deleted. The task tools are registered wherever a chatId is present, so a worker subagent's writes land on the parent chat, and the PTY driver gets a working task surface for the first time. Claiming is atomic: EventStore.decideChatTaskEvents runs the read-decide-append inside the per-chat write chain, replacing the old withFileLock read-modify-write. Leases are still reclaimed on run liveness, never a clock. Both loop prompts, both invariant lists, setup_loop, the wake prompt and the Progress card move onto tasks. setup_loop seeds from a tasks argument or chunk_hint and imports an existing tracking file once, writing no file itself. Also fixes a pre-existing bug where resume_loop dropped parallelism, silently demoting a resumed parallel loop to serial. Removes the file-based loop tracking: the claim, complete and integrate tracking task tools, loop-task-queue.ts, the loop-tracking registry, sync and io adapters, and the file reconcile and skeleton. query_tracking_file, append_tracking_row and replace_tracking_section stay for the non-loop durable-document use.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
An autonomous loop's plan now lives in event-sourced per-chat task state, read and written through the
mcp__kanna__task_*tools, instead of a markdown tracking file. A loop no longer creates, reads, or writesPROGRESS.md.Why this could not be a prompt change
The loop
/clears main on every wake. That nulls the session token — so the next turn is a brand-new CLI session whose own task list is empty andTaskListreturns nothing — and it tripped the client reducer's wipe oncontext_cleared. Three more blockers stacked on top:subagent_entry_appendedon the run, never the parent chat's transcript;--tools, so it had no native Task tools at all;task_createscrolled out of the window.Underneath all four: there was no server-side task state whatsoever.
How
src/shared/chat-tasks/**(types, pure scheduler, projection, compaction, native mirror, seed, tracking-file import) on a newchat-tasksevent log, folded intoStoreStateand the snapshot. Survives/clear, restart, and the tail window.mcp__kanna__task_*— create/update/list/get/note/claim/settle/integrate, registered wherever achatIdis present, so a worker subagent's writes land on the parent chat and PTY gets a working surface.n:id namespace beside Kanna-mintedk:. Oncontext_clearednative tasks are marked stale, not deleted — the CLI really did forget them, but deleting loses visible history.setup_loop, the wake prompt and the Progress card move onto tasks. The wake now carries a rendered<loop-state>snapshot.setup_loopseeds from atasksargument orchunk_hint, and imports an existing tracking file once.TaskProgressSectionmerged intoLoopProgressSection; titled "Progress" when armed, "Tasks" otherwise.Bugs found and fixed along the way
EventStore.appendapplies state after the disk write, so two concurrent claims could both read stale state and take the same task. Fixed withdecideChatTaskEvents, which runs the read-decide-append inside the per-chat write chain. Verified the regression test catches it — reverting the fix makes both workers get the same task id.awaitI added toappendMessage— the hottest write path — destabilised a timing test. Now conditional on there being events to append.resume_loopdroppedparallelism, silently demoting a resumed parallel loop to serial.Removed
claim_tracking_task/complete_tracking_task/integrate_tracking_tasks,shared/loop-task-queue.ts, the loop-tracking registry / sync / io adapters, and the file-based reconcile and skeleton.query_tracking_file,append_tracking_rowandreplace_tracking_sectionstay for the non-loop durable-document use CLAUDE.md documents.Net −2300 lines.
Budgets
deps-bundlesre-baselined 85 → 83 (TaskQueueToolDepsand its adapters deleted;ChatTaskToolDepsadded).max-paramslowered 12 → 11 in botheslint.config.jsandESLINT_LIMIT_PINS— removinggetLoopTrackingtookderiveChatSnapshotto 11, andlint:limitsrequires the ceiling stay tight.Verification
bun run check,bun run test(8260 pass, 0 fail),lint:comments,lint:usestate,lint:limits,check:arch,bunx ast-grep test,check:commits— all green locally.Follow-up needed
c3xis not installed in this environment and ac3-sealcannot be computed by hand, so the ADR (adr-20260918-loop-tasks-replace-tracking-file, referenced in CLAUDE.md and the skill) and the.c3/component bindings for the new modules still need ac3x add adr+c3x repairpass. The deletedloop-task-queue.tsbinding was removed from the unsealedcode-map.yaml/eval/c3-210.yaml.