Todo 52.9 Atomic Report Bundles and Audit Parity Plan
Status: in_progress
Goal
Replace the fundamentally non-transactional two-path report publication with one atomic bundle publication boundary, and close the remaining report/CLI audit gaps found after Todo 52.8.
Root Cause
Publishing flat JSON and Markdown paths requires two independent filesystem operations. If the second operation conflicts, rolling back the first requires a check-then-unlink sequence that cannot atomically prove pathname ownership. A competing process can replace the first path between the inode check and unlink(), so the writer may delete another process's file or return a mixed pair.
Scope
- Fully write and
fsync report.json and report.md inside one private, uniquely named bundle directory.
- Publish the complete bundle through one atomic, no-overwrite directory symlink named from the timestamp; a collision chooses the next numeric suffix without deleting or replacing the competitor.
- Return the two report paths through the published bundle path and leave no flat two-file rollback path.
- Clean only unpublished private bundle state. Never inspect-then-delete a published competitor-controlled pathname.
- Treat mutation after successful publication as out of scope: a process with write access to the output directory can replace any published filesystem entry after the command returns.
- Render runtime errors and semantic failures together in Markdown when both exist, preserving the facts present in JSON.
- Map summary/error stream failures to exit code
2 without exposing secrets or confusing them with benchmark sample failures.
Files
- Modify:
src/agent_lab/infrastructure/benchmark_reporting.py
- Modify:
src/agent_lab/presentation/benchmark_cli.py
- Modify:
tests/test_benchmark_reporting.py
- Modify:
tests/test_benchmark_cli.py
Verification
- Reproduce the Todo 52.8 inode-check-to-unlink race before implementation.
uv run pytest tests/test_benchmark_reporting.py tests/test_benchmark_cli.py -q
- concurrent process/thread bundle publication and collision stress tests
uv run pytest
git diff --check