{"slug": "task-state-is-a-call-stack-for-agent-work", "title": "Task state is a call stack for agent work", "summary": "A developer detailed a technique for managing agent work using a call-stack analogy, where a task-state file records the current frame, local variables, and return address to make deep dives reversible. The approach, implemented in `.local/scratch/task-state.md`, allows both the agent and the developer to checkpoint unfinished tasks before following tangents, ensuring the original work can be resumed. The developer emphasized that task state must be shared between human and agent attention streams, not kept in an agent's internal tracker.", "body_md": "I was one hero image away from publishing [a post about sprint planning](https://www.likhansiddiquee.com/blog/sprint-planning-after-ai/). Generate it, wire the manifest, build the route. Three steps, then done.\n\nI opened the image prompt for one last look and found it arguing with itself. It named colours in the same prompt that told the generator to avoid them. I asked why. That small question pulled us out of publishing and into an audit of every image instruction in the repository.\n\nThe corrected render arrived as a 1.63 MB PNG. I asked another small question: is that normal? Now we were comparing existing heroes, JPEG quality, chroma sampling, and edge artifacts. A second investigation had opened inside the first task.\n\nThe agent did not start either tangent. I did. On another day the agent spots the neighboring defect and asks whether to follow it. The risk is the same: the original task falls out of active context while one of us follows the more interesting question.\n\nAt the bottom of this stack, the post still needed its manifest entry, route build, and removal of a forward link to an unpublished part two.\n\nWe returned to all three because the task had a return address.\n\nA to-do list records what remains. A call stack records more: the current frame, its local variables, and the address where execution should return when a nested call finishes.\n\nThat is what `.local/scratch/task-state.md`\n\ndoes for my agent work.\n\nIn the analogy, the current frame is the immediate question. Its local variables are the working hypothesis, evidence, rejected paths, blockers, and decisions made so far. Its return address is the next step still waiting in the ledger.\n\nBefore either of us changes focus, the instruction tells the agent to checkpoint the ledger. The tangent becomes the current work while the unfinished task remains recorded beneath it. When the tangent closes, we update its result and resume the waiting next step.\n\nIn call-stack terms, the publishing task can be read like this:\n\n``` php\npublish sprint-planning post\n  -> create hero\n     -> I notice excluded colours inside the positive prompt\n        -> we inspect the prompt rules\n        -> return: corrected sidecar\n     -> I question the 1.63 MB render\n        -> we audit the export settings\n        -> return: normalized hero\n  -> wire manifest\n  -> remove unpublished forward link\n  -> build and inspect route shell\n```\n\nThe stack did not stop us from going deep. It made the deep dive reversible.\n\nMost descriptions of agent drift make the agent sound like the only source. It chases a neighboring bug, reads too many files, or turns a local fix into an architecture review.\n\nI interrupt the plan too. I notice a strange output and ask how it happened. I challenge an assumption halfway through implementation. I open a related file while the agent is running and bring a new concern back into chat. Sometimes the side question is more useful than the task that\n\nexposed it.\n\nFrom the work's point of view, ownership of the tangent does not matter. Focus changed while a parent task remained unfinished. The parent needs a snapshot before the new question consumes the active context.\n\nThis is why task state has to be shared. It is not the agent's private plan, and it is not my private set of browser tabs. It is the handoff surface between our two attention streams.\n\nAn agent's internal todo tracker is useful. It can decompose the requested work, mark steps in progress, and show whether the current turn is moving.\n\nI do not treat it as durable task memory.\n\nIt belongs to the active agent session. It may not survive context compaction, a new chat, an editor restart, or a switch to another agent. More importantly, it cannot see a branch that starts in my head. If I inspect an image, question a design choice, or return after lunch with a different hypothesis, the internal plan does not know until I explain the change.\n\nA todo item also tends to preserve the action while losing the state around it. \"Normalize hero\" does not say which settings were tested, why one candidate failed, what still needs visual judgment, or where publishing resumes afterward.\n\nThe local task-state file is visible to both of us. I can correct it. The agent can update it. A new session can read it before reconstructing the task from Git and a partial transcript. Because it lives in the workspace rather than the chat, the work does not depend on one session remaining alive.\n\nI do not rely on remembering to take a snapshot once the tangent has become interesting. This repository's always-on [context-persistence instructions](https://github.com/lsiddiquee/mysite/blob/main/.github/copilot-instructions.md#context-persistence-never-lose-work-on-tangents) carry the protocol. Copilot reads that file as repository guidance, so the checkpoint triggers arrive with each new session rather than remaining in the chat that may disappear.\n\nThe instruction is explicit:\n\n`.local/scratch/task-state.md`\n\nexists before the first edit. Create it only when absent.`.local/scratch/task-state-completed.md`\n\nand promote any durable finding into tracked documentation in the same pass.This is how I ensure snapshotting happens at the transition, not as cleanup after context is lost. It is still an instruction, not a transactional runtime guarantee. Its advantage is that the rule survives the session, the artifact is visible to both of us, and a missed checkpoint is reviewable.\n\nThe storage choice is part of the same instruction. This repository explicitly lists `.local/`\n\nin `.gitignore`\n\nbecause the contents are local until promoted. A current hypothesis, unfinished frame, or rejected path should not be pushed to every contributor as project guidance.\n\nGitignored does not mean disposable. The devcontainer bind-mounts the repository workspace from the host. Rebuilding the container replaces the environment, not `.local/scratch/task-state.md`\n\ninside that workspace. The stack is still there when the new container opens.\n\nI do not use VS Code chat memory as that recovery layer. A transcript may remain visible, but a new chat is not guaranteed to load it as working context; compaction can also remove the detail needed to resume. The task-state file has a stable path that the repository instruction tells the next session to read.\n\nThe ledger has no fixed schema. This run uses `Current focus`\n\n, `Ledger`\n\n, `Chosen metadata`\n\n, and `Boundaries`\n\n; older states also use `Key findings`\n\n, `Remaining`\n\n, and `Gotchas`\n\n. The headings follow the work.\n\nThere is no literal stack API or required `Return`\n\nfield. I read `in-progress`\n\nas the top frame, a pending item plus its next step as the return address, and open threads and findings as the state needed to resume. Completing the tangent and returning to that pending item behaves like a pop. Finished history moves into `task-state-completed.md`\n\n; reusable findings are distilled into their tracked owner.\n\nMost of the time I do not open the task-state file myself. I ask the session:\n\nWhat are my pending items?\n\nThe agent reads the ledger, reconciles it with the working tree, and gives me the unfinished items with their current status. I choose one. That item becomes the current work, and the session already has the findings, blockers, and next step recorded around it.\n\nThe question works in the same chat, after a context compaction, or in a new session because the answer comes from the workspace artifact. I am not asking the model to recall an old conversation. I am asking it to query shared state, then continue from the item I select.\n\nMarkdown under `.local/`\n\nis how I implement the idea in these repositories. It is not the only way. An issue, worktree note, session database, or orchestration system can carry the same state.\n\nThe direction matters more than the storage choice:\n\nWithout those properties, changing the filename does not solve the handoff problem.\n\nI wrote about AI acting as a\n\n[velocity microphone](https://www.likhansiddiquee.com/blog/velocity-microphone/): it amplifies unclear intent and returns the consequences faster. It also makes investigation cheap. A side question can become a twenty-file audit before either of us feels the cost.\n\nI do not want to remove that curiosity from the workflow. I want a reliable way back.\n\nThe ledger preserves the parent task's return address through context compaction, agent changes, and my own detours.", "url": "https://wpnews.pro/news/task-state-is-a-call-stack-for-agent-work", "canonical_source": "https://dev.to/likhan/task-state-is-a-call-stack-for-agent-work-a22", "published_at": "2026-08-31 20:19:20+00:00", "updated_at": "2026-08-31 20:53:21.343482+00:00", "lang": "en", "topics": ["ai-agents", "developer-tools", "mlops"], "entities": [], "alternates": {"html": "https://wpnews.pro/news/task-state-is-a-call-stack-for-agent-work", "markdown": "https://wpnews.pro/news/task-state-is-a-call-stack-for-agent-work.md", "text": "https://wpnews.pro/news/task-state-is-a-call-stack-for-agent-work.txt", "jsonld": "https://wpnews.pro/news/task-state-is-a-call-stack-for-agent-work.jsonld"}}