The Work Ledger: Turning Agent Work Into Verified Transactions
This draft was rewritten with Codex from an audit-grounded Soul CLI development note, using local repository docs, recent commits, and command-surface inspection.
The more I use agents for real engineering work, the less I trust the chat window as the source of truth.
This is not a complaint about model quality. Codex is useful. Gemini is useful. Claude is useful. A direct shell session is useful. The problem is that each of these tools is only an execution surface. They are places where work happens, not durable places where the meaning of the work should live.
If the terminal dies, the task should survive.
If the model changes, the evidence should survive.
If I switch from Codex to Gemini for review, the work should still be the same work.
That is the architectural pressure behind the latest soul-cli changes. I have been turning ad hoc agent sessions into a local work ledger: tasks, runs, worktrees, verifier steps, reviews, plans, and provider routing all tied together by the registry.
The goal is not process for its own sake. The goal is to make agentic work recoverable.
The Problem: AI Work Has No Native Receipt
Most AI-assisted development still works like this:
- Open a chat or terminal agent.
- Ask it to make a change.
- Watch it edit files.
- Run a test.
- Maybe ask for a review.
- Summarize what happened in prose.
This is fine for small loops. It breaks down the moment the work becomes long-lived.
What was the task? Which run attempted it? Which checkout did it use? Which test passed? Which reviewer inspected it? Which provider executed the review? Which branch contains the work? Which evidence closed the loop?
Without a ledger, all of that becomes “somewhere in the transcript.”
That is not good enough for a system I rely on as personal infrastructure.
The Rejected Paths
Before landing on the current shape, there were a few tempting but wrong answers.
Just Use Git Branches
A branch is not a task. A branch can hold a diff, but it does not explain the work contract. It does not know the Done criteria. It does not know which verifier step passed. It does not know whether a specialist review happened or timed out.
Git is excellent at code history. It is not a complete execution ledger.
Just Use Chat History
Chat history has the opposite problem. It preserves too much narrative and too little structure. The important facts are in there, but mixed with false starts, partial reasoning, UI noise, and context that may disappear after compaction.
It is useful as forensic material. It should not be the primary database.
Just Let Each Provider Own Its Own State
This is the most dangerous option because it feels convenient. Codex can manage a task. Gemini can manage a task. A desktop app can manage a task. But then the task is not actually local-first anymore. It becomes provider-scoped.
I want the opposite: providers should execute work at the edge, while the registry stores the durable state.
The Chosen Shape: A Work Transaction
The core workflow now looks like this:
task -> run -> worktree -> implementation -> verifier -> review -> commit -> merge -> close
That is the purpose of soul work.
It is the happy-path wrapper around lower-level primitives:
soul taskstores the human contract.soul runstores the execution attempt.soul workspacecreates and tracks isolated git worktrees.soul delegatedispatches specialist review through providers like Gemini or Codex.soul work verify,review,commit,merge, andclosekeep the phases connected.
The wrapper is not meant to hide the primitives. If something fails, I can still drop down into soul task, soul run step, soul workspace, soul delegate, or raw git. The wrapper exists because the normal path should preserve accountability by default.
Tasks: The Contract
A Soul task is the human-level contract for the work.
It has a stable id like SOUL-SOUL-400 or SOUL-KLAWEHT_BLOG-004. It carries a subject, description, priority, category, contract type, status, and Done criteria.
That sounds simple, but it changes the shape of the system. The task is no longer buried inside a prompt. It is a registry object.
If I ask an agent to continue tomorrow, it can inspect the task instead of reconstructing intent from a transcript. If there were multiple attempts, the task remains the same contract while each attempt gets its own run.
This is the first important separation:
task = what should become true
run = one attempt to make it true
Runs: The Attempt
A run is the execution envelope.
It records the run_id, the owning task, the session that started it, the objective, the workspace, the verifier steps, review steps, artifacts, and lifecycle status.
This matters because real implementation is messy. A task might have:
- an accidental run that gets cancelled;
- a failed verifier run;
- a second run that fixes the issue;
- a final closeout run that merges and cleans up the worktree.
Without runs, all of that gets flattened into one vague task status. With runs, the system can preserve the operational history without corrupting the task contract.
The task stays stable. The attempts stay honest.
Worktrees: The Execution Cell
The worktree piece is where the architecture starts paying for itself.
When soul work begin starts implementation, it can create a Soul-owned git worktree under:
~/soul_worktrees/<project>/<task-id>
The branch name is derived from the task, for example:
codex/soul/soul-soul-400
codex/klaweht-blog/soul-klaweht_blog-004
The workspace record stores the parent repo, parent branch, base ref, base commit, task id, branch, path, and portable path reference.
This solves a very practical agent problem: providers inherit local state. If my primary checkout is dirty, or sitting on some unrelated feature branch, an agent can accidentally build on the wrong base.
The newer soul work tests make this explicit. If the primary checkout is dirty on a feature branch, the task worktree can still spawn from a clean local base branch. The parent checkout remains untouched. The agent gets a clean execution cell.
That gives me three guarantees:
- The task branch is isolated.
- The run knows exactly where the work happened.
- Closeout knows what can be merged back.
This is why I do not think of worktrees as a git convenience. In this architecture, the working directory is part of the contract.
Review: A State Transition, Not a Vibe
Review is another place where agent workflows usually get soft.
Someone says “looks good” in a chat. Or the agent says it reviewed its own work. Or a second agent leaves a paragraph somewhere in the transcript.
That is not enough.
In Soul, review is a durable run step. soul work review delegates to a specialist and records the result under the run. The step can carry status, summary, provider metadata, dispatch details, and a finding path.
The closeout path depends on that evidence. By default, soul work commit expects completed verifier and review evidence. soul work merge requires verified merge evidence. soul work close can automatically merge a Soul-owned task worktree back into its recorded local target only after verifier and review evidence exist.
That is the important shift:
review is not a sentence
review is a recorded transition
The implementation also had to account for reality. Reviews can timeout. A delegated subprocess can be interrupted. A review step can get stuck in running. The recent hardening work records those failures instead of leaving ghost state behind.
I do not need every subagent to be perfect. I need every subagent to leave a receipt.
Plans: Freezing the Strategy Before Execution
For broader work, I added plan-bound execution.
The pattern is an Advisor/Executor split:
- The Advisor audits the codebase.
- The Advisor writes a Markdown plan.
- The plan is reviewed against real code.
soul work begin --plan <path>turns the plan into a task, run, artifacts, and pinned worktree.- The Executor implements inside that bounded contract.
The plan contains a title, Planned at commit, in-scope paths, out-of-scope paths, Done criteria, verification commands, STOP conditions, and review notes.
The parser is intentionally strict where it matters. Scope paths cannot be absolute. .. traversal is rejected. Planned at must resolve to a real commit. The run preserves both the original plan.md and the parsed plan.json.
This reduces improvisation.
Instead of asking an executor model to rediscover strategy from conversation residue, the executor gets a sealed contract. It knows what files are in scope, what commands prove the work, and when it should stop.
That is especially important when moving work across providers. A plan written in one session can be executed by another runtime without depending on the original context window.
Providers: Execution Surfaces, Not Memory Owners
The provider layer is where this becomes more than a local task runner.
Codex and Gemini have different command shapes, different model knobs, and different stream formats. Soul does not pretend otherwise.
For Gemini review dispatch, the system uses the Gemini CLI prompt surface, stream JSON output, trust/include-directory controls, and environment-based reasoning effort.
For Codex dispatch, it uses codex exec --json --skip-git-repo-check, safe model forwarding, and model_reasoning_effort configuration.
The important part is compatibility filtering. A Gemini model name should not be passed into Codex. A Codex model name should not be passed into Gemini. Runtime environment hints are useful, but they are not absolute authority. A Codex-authored diff can still ask for a Gemini-pinned reviewer.
The dispatch preflight now makes the choice inspectable:
provider=<resolved provider>
provider_source=<why that provider was chosen>
model=<requested model>
dispatched_model=<safe model actually sent>
model_resolution=<how compatibility was handled>
reasoning_effort=<requested/native effort>
This is the provider-agnostic model I want: not one generic abstraction that erases differences, but a shared registry that records the meaning of the work while provider-specific behavior stays at the edge.
What This Buys Me
The practical payoff is that I can ask better questions.
Instead of:
What happened in that agent session?
I can ask:
- Which task was this?
- Which run attempted it?
- Which worktree contains the diff?
- Which verifier step passed?
- Which review step inspected it?
- Which provider executed the reviewer?
- Which model was requested, filtered, or inherited?
- Which branch was merged back?
- Which evidence closed the work?
That is a different level of control.
The operator stops acting as the memory layer for a pile of transcripts. The operator becomes the architect of a local execution system.
The Bottom Line
The unit of serious AI work is not a prompt.
It is a verified transaction.
Tasks preserve intent. Runs preserve attempts. Worktrees preserve isolation. Reviews preserve independent judgment. Plans preserve bounded strategy. Provider routing preserves execution portability.
This is why the latest soul-cli work looks like infrastructure instead of another chat feature. The point is not to make agents feel more autonomous. The point is to make their work accountable enough that I can trust it after the context window is gone.
That is the threshold I care about: not whether an agent can impress me in a single turn, but whether the work can survive the provider, the terminal, the branch, and the day.