cd /news/ai-agents/saw-15-agentrail-correcting-the-reco… · home › topics › ai-agents › article
[ARTICLE · art-152408] src=blog.softwarewrighter.com ↗ pub= topic=ai-agents verified=true sentiment=↑ positive

Saw #15: agentrail, Correcting the Record

Agentrail-rs PR #3 adds three commands to the agentrail tool — `close`, `edit` and `reserve`/`unreserve` — plus a cursor fix and a new report, after the saga on the X_eTaL typed array language grew to 150 steps and drifted from git history. The drift came from work finished in another repository (steps 101 to 104 in X_eTaL-libraries), five steps done in a parallel lane and merged in one pull request, and step 111's prompt describing a `cr"..."` form later dropped from the design. The author rejected the existing `reopen`-then-`complete` route because a test on a copy of the saga showed step 100 picking up a new timestamp and an unrelated commit, and fixed a second bug where `complete` advanced the cursor onto already-finished work.

read13 min views1 publishedOct 18, 2026

3129 words • 16 min read • Abstract

Resource Link
The change agentrail-rs PR #3 — close, edit, reserve; the cursor skips finished work
The tool agentrail-rs
Earlier Saw #3: agentrail-rs ·AI Tools #2: insert, reorder, reopen
Comments Discord

The saga, and its cursor #

An agent starts every session with agentrail next, marks the step begin, does the work, commits, and runs agentrail complete. That records the commit, a summary and a trajectory, then moves the cursor to the next step. The saga is a record of the git history, and the next session trusts it.

That model assumes the work happens in order, here, by the agent holding the cursor.

How the record drifted #

On X_eTaL, the typed array language this blog’s Array Languages series follows, the saga had grown to 150 steps, and three things had happened to it:

  1. Work moved to another repository. Four steps of the equation work, 101 to 104, were finished in X_eTaL-libraries after the library moved there. In X_eTaL’s saga they were stillpending , and the cursor had moved on to 111.
  2. Work was done in a parallel lane. Five steps of readable error messages were done in a lane with its own saga and merged in one pull request. The main saga still listed them aspending , ahead of the cursor.
  3. The plan changed after a step was written. Step 111’s prompt described a raw form of the new code strings,cr"..." , that was later dropped from the design.

None of that is unusual once several agents share a project. Each one is a step whose record no longer matches the history.

Why the existing commands could not fix it #

agentrail already had commands for changing the plan mid-saga, insert, reorder and reopen, all of which protect completed steps. That is right, because completed steps anchor the git history. But the protection also boxed in the stranded steps:

  • reorder 101 --to 111 refuses, because steps 105 to 110 between them are completed, and completed steps never renumber.
  • reopen 100, then complete, moves the cursor to 101, but it rewrites step 100’s completion time to today and appends today’s commit to step 100’s record. Completing 101 then credits X_eTaL’s latest commit with work that lives in another repository. It corrupts two records to fix one.
  • Hand-editing a step’s file is forbidden by the project’s own rules, and rightly: the saga is append-only state, and a hand edit is how the record and the history drift apart silently.

The reopen route was tried first on a copy of the saga, and the result diffed: step 100 picked up a new timestamp and an unrelated commit. That was enough to decide that the tool, not the saga, needed fixing.

And there was a second bug in the same place: complete advanced to the step after the current one whether or not that step was already done, so the cursor could land on finished work.

What changed #

Three commands, one fix to the cursor, and one new report.

Command What it is for
agentrail close N --summary S (--commit REF … \| --elsewhere WHERE) complete one step out of order, recording where the work is
agentrail edit N --prompt P replace the prompt of a step not yet done
agentrail reserve N --for OWNER /unreserve N hand a step to someone else before the work starts

close: a step done elsewhere

agentrail close 101 --summary "Spatial oracle, done in the libraries repo" \
  --elsewhere "X_eTaL-libraries@0f82f93"

agentrail close 126 --summary "Readable errors, lane readable" \
  --commit 7c1e0a2          # a commit in this repository

close marks a pending, in-progress or blocked step completed, out of order, and makes the agent say where the work is: --commit, repeatable, for commits in this repository, resolved to full SHAs so agentrail audit can match them; or --elsewhere for anything else, stored with the step as done_elsewhere. Passing neither is an error. close never records the current commit on its own and never touches another step. If the closed step is the current one, the cursor moves on.

edit: a step whose plan changed

agentrail edit 111 --prompt docs/code-strings-step-111.md

This replaces the prompt and the description status shows. A completed step’s prompt is history, so edit refuses one; reopen it first.

reserve: preventing the drift

agentrail reserve 111 --for "cloud agent, PR to follow"
agentrail close 111 --summary "Code strings" --commit <merge-sha>

When a step goes to someone else, another lane, a cloud agent, another repository, it is reserved before the work starts. The cursor skips a reserved step, complete never lands on it, and status shows it as [r] … (reserved for cloud agent). Closing it ends the reservation; unreserve takes it back.

The cursor, and stranded steps

complete now advances to the next open step, one that is neither completed nor reserved, and says what it skipped. If none is left, it creates the next step after the highest number.

And status and next now list every pending, unreserved step behind the cursor, because nothing will ever bring the cursor back to it, with the fix for each:

Stranded: 4 pending step(s) behind the cursor (111):
  101-spatial-oracle
  102-spatial-capture
  103-spatial-lowering
  104-spatial-latex
  Done elsewhere?  agentrail close N --summary ... --commit REF | --elsewhere WHERE
  Someone else's?  agentrail reserve N --for OWNER
  Still to do?     agentrail reorder N --to M (if no completed step lies between)

A silent inconsistency becomes a visible to-do with the command attached.

Reading the record #

An agent’s full conversation stays on the machine it ran on. What agentrail commits with the code is the part worth keeping: what was planned and what was done, in a few sentences per step. Anyone can read it, on GitHub or in a clone, without agentrail installed.

Where the words are

Everything is in the .agentrail/ folder at the top of the repository. In X_eTaL’s, for example, .agentrail/steps holds one folder per step, named by number and slug, 110-macro-contracts. Four files tell the story:

File What it says
.agentrail/plan.md the saga’s plan, in prose: its goal and what was decided
steps/NNN-slug/prompt.md what the step was asked to do, written before it started
steps/NNN-slug/summary.md what was done, a few lines written when it finished
steps/NNN-slug/step.toml when it finished, the commits it made, or where it was done instead

So the way to read a step is in that order: the prompt, then the summary, then a glance at the record for when and where. Step 110 of X_eTaL’s saga, the macro contracts, reads like this. Its prompt, the plan, begins:

The user (2026-10-17, decided): three contract lines in a macro's doc block, checked by
the macro runner ... ## uses: copied (the default: every name and number token of each
argument appears as a token in the expansion, else error[unused-argument] at the argument
naming the text ignored ...) and ## uses: parsed (the macro parses a notation and takes
responsibility ...). Test-first ...

Its summary, what was done:

Macro contracts (MC31, D163): xetal-contract crate (## takes:, ## uses: copied|parsed,
## binds:) checked by the expander around each call; bad-macro-call before the macro runs,
unused-argument after, bad-contract at load; every standard macro annotated; xetal doc
shows takes/uses; spec + goldens; docs. ... Commit 57cff932 pushed.

And its record says when, and which commit holds the work:

completed_at = "2026-10-17T16:51:51"
commits = ["57cff93220e03f551567200f8812d78a9f9b1b8a"]

On GitHub, the History button on the .agentrail folder lists the saga’s own commits, one per finished step, chore: complete step 110 (macro contracts), and each commit in commits links to the code itself.

All of it at once

In a clone, agentrail history prints every step in order, its status, the first line of its prompt and its summary:

108-plain-names [production] (completed)
  The user (2026-10-17), after a survey of names: rename what reads oddly or de...
  Summary: Plain names (D161): s:r_ange -> s_pread, demos/sub.xtl -> dyadic.xtl, Macros t_h...

109-spatial-pipeline-macro [production] (completed)
  Saga 41 step 5b (docs/plan.md, docs/spatial.md Pipelines, D155): e:p_ipe< in ...
  Summary: Handed to X_eTaL-ML (the user's choice: the pipeline macro lives beside the nn: ...

110-macro-contracts [production] (completed)
  The user (2026-10-17, decided): three contract lines in a macro's doc block, ...
  Summary: Macro contracts (MC31, D163): xetal-contract crate (## takes:, ## uses: copied|p...

It does not print dates. For a diary of what was done when, a few lines of shell put each finished step’s date beside its summary:

for d in .agentrail/steps/*/; do
  t=$(sed -n 's/^completed_at = "\(.*\)"/\1/p' "$d/step.toml")
  [ -n "$t" ] && [ -f "$d/summary.md" ] &&
    printf '%s  %s  %s\n' "${t%T*}" "$(basename "$d")" "$(head -c 70 "$d/summary.md")"
done | sort

The last three days of X_eTaL, from that:

2026-10-16  100-raw-strings       Raw strings (ST4, D156): lexer r"..." and r#"..."# (verbatim, mult
2026-10-16  106-boxed-macro-text  Boxed broke every macro (expansions captured through the boxed pr
2026-10-17  107-expand-layout     xetal expand and Expand lay a macro's text out as a program past 8
2026-10-17  110-macro-contracts   Macro contracts (MC31, D163): xetal-contract crate (## takes:, ##
2026-10-18  101-spatial-oracle    Done in X_eTaL-libraries as its Saga 12 step 2 (equations-oracle):
2026-10-18  115-shape-lint        The shape lint (shape-mismatch, shape-check-proven, r_equireShape)
2026-10-18  126-errors-located    Saga 27 step 1 done in the readable lane (lanes/readable/.agentrai

One thing to read carefully. A step closed out of order carries the date it was recorded, not the date the work was done: step 101 shows the 18th because that is when the record was corrected. Its summary says where the work is, and its record names the commit, done_elsewhere = "X_eTaL-libraries@8197d52 (Saga 12 step 2)", whose own date is when the work happened.

The correction, told in words

The correction itself reads the same way. It was the first use of the new commands, a minute after the change merged, and its commit message is a plain account of what was set right and where each piece of work really is:

saga: correct the record (steps done elsewhere closed, 111's prompt, 119 reserved)

Using agentrail's new close/edit/reserve (sw-vibe-coding/agentrail-rs
PR #3), each with where the work is, no other step touched:

- 101-104 (Saga 41 steps 2-5): done in X_eTaL-libraries as its Saga 12
  (8197d52, 09143d8, cb430d5, ba03d6a); closed --elsewhere.
- 114 integrals-plan: Saga 42 planned and decided (18c1501f, 6c0bf1c9).
- 115 shape-lint: merged with PR #130 (dc359072).
- 120 doc-comment-glyphs: c005fa16.
- 123 errors-library-calls (ask E5) and 126-130 (Saga 27): done in the
  readable lane, merged with PR #131 (80087dab).
- 119 pages-gh-pages reserved for the local agent: the build moved to
  gh-pages (b025b3d0); switching GitHub Pages' source remains.
- 111 code-strings: prompt corrected to the decided design (c"..." and
  c#"..."#; cr"..." was dropped) and docs/code-strings.md's order.

agentrail status reports no stranded steps; the cursor stays at 111.

Half an hour later the local agent switched the Pages setting and closed step 119, and its summary says what it did, in a sentence:

GitHub Pages' source switched from the main workflow to the gh-pages branch (gh api,
2026-10-18 07:40) after the first just publish from main 723cd4eb; the site build moved
there in b025b3d0 (#128, D164).

Reserved, done and closed, and each step of it written down in words someone else can read.

And the full transcripts #

The record above is the summary. The whole conversation, every request, reply and command, is kept by the coding agent itself, not by agentrail, and since the work runs in parallel it is spread across machines. With Claude Code:

On a Mac or Linux machine, each session is a file under~/.claude/projects/ , in a folder named after the repository’s path with its slashes turned into dashes. For X_eTaL on my Mac:

~/.claude/projects/-Users-mike-github-softwarewrighter-X-eTaL/d545dc14-38c5-4323-80b6-15b75c666593.jsonl

The Arch Linux machine keeps its own sessions the same way, in its own home folder. Each file holds one JSON object per line, and jq pulls out the readable part, the agent’s replies with their times:

jq -r 'select(.type=="assistant") | .timestamp as $t
       | .message.content[]? | select(.type=="text") | "\($t)  \(.text)"' SESSION.jsonl

From the X_eTaL session above, on the evening step 110 was being built:

2026-10-17T21:13:22.930Z  Libraries step 1 is done and published. Next in the chain: X_eTaL step 110 (macro contracts).
**Step 110, macro contracts (in progress):** the `xetal-contract` crate is written and green (10 tests),

In Claude’s cloud, a session runs on a virtual machine that is gone when it ends, so its transcript is not on any disk of mine; it is in the web interface, at claude.ai/code. A commit made there carries a trailer that links back to it, as the correction above does:

Claude-Session: https://claude.ai/code/session_01Etyh3Gka9iV2U9SJkNnmhm

That trailer is how a commit leads back to the conversation that made it. The screenshot beside this section is one such session, reporting on X_eTaL’s step 111 this morning: it lives only in the web interface, and no folder on my machines holds it.

Transcripts are long, private and scattered; that is why the saga keeps the summary. The record in the repository says what was planned and done, the commits hold the code, and a transcript, wherever it lives, is there for the rare time the reasoning behind a step matters.

The rules behind it #

  • Make the agent say where. The one thing a record of work must never do is invent where the work came from.close refuses to guess, so the record says “done in X_eTaL-libraries at 0f82f93”, not “done at whatever commit happened to be checked out”.
  • Touch one step. Every correction changes exactly the step it names. Completed steps still never renumber, and nothing rewrites a neighbor’s history.
  • Prevent as well as repair.close repairs,reserve prevents, and the stranded report finds what slipped through.
  • Keep every existing saga working. The two new fields,reserved_for anddone_elsewhere , are optional and left out of the file when unset, so existing step files load and save back byte for byte, which a test checks. Older agentrail binaries ignore the fields.
  • Teach the agents. agentrail hands every project a shared set of agent instructions, and the recovery part now says: correct the record with commands, never by hand-editing or by reopening a neighbor, and reserve a step when you hand it off.

By the numbers #

  • 3 new commands, 1 fix to the cursor, 1 new report, in one pull request of about 700 lines.
  • 11 new tests; the workspace’s 192 tests pass, with clippy and rustfmt clean.
  • Tried first on a copy of X_eTaL’s real 150-step saga: the report found the 4 stranded steps, and close changed only the step it named.

The lesson #

A workflow tool that keeps agents honest has to make it easy to be honest about work it did not see. Once several agents share a project, “done somewhere else” is a normal state, not an exception. The record needs a way to say so, and the tool should require the evidence.

Part 15 of the Sharpen the Saw Sundays series. View all parts

Comments or questions? SW Lab Discord or YouTube @SoftwareWrighter.

── more in #ai-agents 4 stories · sorted by recency
── more on @agentrail 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
→ Live at https://your-agent.zahid.host ✓
Get free account → Pricing
from €0/mo · no card required
LIVE [news/saw-15-agentrail-cor…] indexed:0 read:13min 2026-10-18 · —