Skip to content

fix(route-insight): never overwrite an earlier backup - #191

Merged
chuycepeda merged 2 commits into
The-AIOS:mainfrom
matiasmacera:fix/route-insight-unique-backup
Sep 29, 2026
Merged

chuycepeda merged 2 commits into
The-AIOS:mainfrom
matiasmacera:fix/route-insight-unique-backup

Conversation

@matiasmacera

Copy link
Copy Markdown
Contributor

What

hooks/route-insight.py saves a backup before it rewrites a file, named <file>.routebak-<YYYYmmdd-HHMMSS> from the file's mtime. The name now can never replace an existing backup: it is claimed with O_CREAT | O_EXCL, and when taken the next free -2, -3, … suffix is used. If the copy then fails, the claimed placeholder is removed before the error is re-raised, so an empty file never reads as a backup. The new names still match *.routebak-*.

Why

The name has one-second resolution. A close that routes several entries in a row rewrites the file within the same second each time, so from the second run on, each backup landed on the previous one's name and replaced it. Only the last pre-image survived, and the first one, the only copy still holding every entry routed away, was lost with no error.

Proof

New scenario in tests/route-insight.test.sh: three entries routed with the file's mtime pinned to the same second before each run, which makes the collision deterministic. It checks that three backups exist and that one still holds the first entry.

Run Result
bash 3.2.57 (/bin/bash) 13 passed, 0 failed
bash 5.3 13 passed, 0 failed
same suite against the current main tool 11 passed, 2 failed (one backup left, first pre-image gone)
tests/lint-windows-stdout.py pass
full tests/*.test.sh battery 87 of 87 suites passed (a nested claude -p check in headless-allowlist hung locally and was skipped)

Not in this PR

Two runs on the same file at the same time are still not serialised: one can copy a half-written file, or restore its backup over the other's write. Fixing that needs a per-file lock around the whole read-backup-write-verify sequence, which is a larger change than this one. This PR only covers runs in sequence, which is how a close routes entries.

🤖 Generated with Claude Code

matiasmacera and others added 2 commits September 26, 2026 12:30
The backup name has one-second resolution, so routing several entries in
a row made each backup replace the previous one, and the first pre-image
was lost. Claim the name with O_EXCL and take the next free -N suffix;
remove the placeholder if the copy fails. Concurrent runs on one file are
still not serialised and are left for a separate change.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
chuycepeda added a commit that referenced this pull request Sep 29, 2026
Covers #187-#191 and their follow-ups. Manifests move to 0.8.6.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCjeYxWVWosFKnRiCf8Ae7
@chuycepeda
chuycepeda merged commit d6d9869 into The-AIOS:main Sep 29, 2026
16 checks passed
@chuycepeda

Copy link
Copy Markdown
Member

Thank you, Matías. This shipped in v0.8.6 (merged via #195). O_EXCL with a -N suffix is the right shape, and cleaning up the placeholder when the copy fails is the detail that is easy to miss. Agreed that serialising concurrent runs on the same file is a separate change.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants