Skip to content

v0.6.1 — three event-log findings the audit left open

Latest

Choose a tag to compare

@waazy-w waazy-w released this 22 Aug 08:22
· 22 commits to main since this release

Three event-log findings from the original audit that were never actually fixed. The audit was reported closed while these were still open; two of them lose data.

A torn last line swallowed the next event

appendFileSync concatenates onto whatever is already there, so a log whose final line lost its newline — a writer killed mid-write, a full disk — merged the next event into the broken one and left both unparseable.

The crash simply never reached Claude or the status line, and the log did not self-heal until the next trim. Proven in a sandbox: the event vanished entirely, leaving one event where there should have been two.

The tail is checked and healed before appending now. Losing the torn record itself is the unavoidable cost of the tear; losing the next one was the bug.

One unreadable line replayed every event the user had already seen

The cursor was a bare event id, and findIndex returning -1 was treated identically to "never acknowledged anything" — so if the cursor's own line became unparseable while its neighbours survived, the entire retained log came back as unread: a 200-event briefing dumped into the next prompt, and (+199) on the status line. Measured at 39 already-seen events replayed.

The cursor records its timestamp now, so a missing id degrades to "newer than the acknowledged time" instead of collapsing to "everything". The old single-field format is still understood, so an existing cursor keeps working.

The plugin ships its own copy of the reader and moved with it — it runs without src/ on disk, so the two have to agree on the cursor format or the plugin replays everything.

Getting the test right took three attempts

Worth recording, because the first two both reported "fixed" against the unfixed code.

When a cursor falls out of a trimmed log, the events it acknowledged have usually fallen out too — so there is nothing left to replay and the bug does not show. A scenario built that way passes either way round. Only a single corrupted line with intact neighbours discriminates: 39 replayed versus 0.

Two vacuous tests would have shipped without a mutation check on each attempt.

Documentation

The "not a sandbox" note now says what the lexical guard actually cost. It described the guard as lexical without conveying the consequence: an audit found four ways past it in a single afternoon — a recursive grep that names no path, git show HEAD:.env, a redirect written without a space, and a shell glob the matcher expanded differently from the shell.

All four are fixed and pinned by tests. The README now says plainly that a lexical guard is a speed bump, that a fifth way probably exists, and that the real backstops are the ones which do not depend on parsing a command correctly: branch isolation, the post-session audit, and the no-push rule.

Known limitation

One cursor per repository. With two Claude Code windows open on the same repo, whichever prompts first consumes the crash notification and the other never sees it.

A per-session cursor would need matching state in the plugin's own copy of the reader and a pruning story for abandoned sessions. Recorded here rather than half-built.

Notes

458 → 461 tests. CI green on all 15 jobs.

Full changelog: v0.6.0...v0.6.1