Skip to content

fix(snapshot): a signal ends the run; copies land whole or not at all - #186

Merged
chuycepeda merged 2 commits into
The-AIOS:mainfrom
matiasmacera:fix/snapshot-signal-and-partial-copy
Sep 25, 2026
Merged

chuycepeda merged 2 commits into
The-AIOS:mainfrom
matiasmacera:fix/snapshot-signal-and-partial-copy

Conversation

@matiasmacera

Copy link
Copy Markdown
Contributor

What

Two ways an interrupted hooks/aios-snapshot run leaves the archive wrong. Both are silent.

# What happened
1 trap release EXIT INT TERM released the lock on INT or TERM, and the script then continued: bash runs the handler and resumes the loop. The remaining files were archived with no lock, which is the one thing the lock exists to prevent.
2 Each copy went straight to the final snapshot name. A copy cut short, by a full disk, a failing cp or a killed process, stayed under that name. Every later run compared against the truncated file, and it read as history.

Fix

  1. trap release EXIT, plus trap 'release; exit 130' INT and trap 'release; exit 143' TERM. A signal that arrives during a foreground cp is handled when the cp returns. The handler discards that copy and exits.
  2. put copies to .aios-snapshot.<pid>.tmp in the snapshot directory, then renames it to the final name. The rename is atomic within one filesystem, so a final name only ever holds a complete copy. The temp name starts with a dot and cannot match the variant globs. The temp file is removed on failure and in release. Callers still check under the lock that the destination does not exist, so the collision logic is unchanged.

A power cut is not covered: nothing calls fsync. What is covered is every interruption where the process stops but the filesystem survives.

Proof

tests/aios-snapshot.test.sh gains a section with cp shims on PATH:

  • A cp that writes three bytes and fails. Nothing is left under a snapshot name, and no temp file remains.
  • A cp that writes three bytes and SIGKILLs the archiver. No trap runs, so only the write path decides what stays. No snapshot name exists afterwards.
  • TERM and INT during a slow cp. Each exits 143 or 130 and archives nothing more. The lock is released and no temp file remains. Job control is enabled for that invocation, because a non-interactive shell starts background jobs with SIGINT ignored.
  • The same fixtures against the hook pinned at the pre-change commit. The truncated file stays as 2026-08-14-obs.md, and after TERM the second file is still archived. These cases are skipped with a message if the commit is missing.
  • Mutation controls. A trap without exit, INT without exit, a direct copy instead of put, and no temp cleanup each fail their case.

22 pass under bash 5 and bash 3.2. The existing concurrency race, with the lock as the only difference between variants, still passes, and its control still fires. The full tests/ run passes.

Not in this PR

The lock-reclaim race (two processes reading the same dead pid) has the same shape in aios-commit, aios-snapshot and aios-note-append. It gets its own PR across all three.

Scope

hooks/aios-snapshot, its test, and the CHANGELOG entry. Part of the hooks series announced in #158.

🤖 Generated with Claude Code

matiasmacera and others added 2 commits September 25, 2026 17:01
- `trap release EXIT INT TERM` released the lock on a signal and the loop
  kept archiving the remaining files with no exclusion. INT and TERM now
  release and exit (130 / 143).
- Copies went straight to the final name, so a copy cut short stayed as a
  valid-looking snapshot that every later run compared against. `put`
  copies to a hidden temp file in the same directory and renames it into
  place; the temp file is removed on failure and on release.

tests/aios-snapshot.test.sh: a cp shim that fails halfway, one that
SIGKILLs the archiver mid-copy, and TERM/INT during a slow copy (job
control on so INT reaches the background job). Each case also runs
against the hook pinned at the pre-change commit, where it reproduces
the defect.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
chuycepeda added a commit that referenced this pull request Sep 25, 2026
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LCjeYxWVWosFKnRiCf8Ae7
@chuycepeda
chuycepeda merged commit 2c566a2 into The-AIOS:main Sep 25, 2026
16 checks passed
@chuycepeda

Copy link
Copy Markdown
Member

Shipped in v0.8.5 with your commits and authorship intact. Thank you. It went in as you wrote it: 22/22 under bash 5 and 3.2, and 6 of them fail on the old hook. Looking forward to the lock-reclaim PR across the three hooks.

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