summaryrefslogtreecommitdiff
path: root/docs/superpowers/followup/pr3840-write-report-atomicity.md
diff options
context:
space:
mode:
authorhathach <[email protected]>2026-08-21 22:16:25 +0700
committerhathach <[email protected]>2026-08-21 22:25:13 +0700
commitaa4a07820dd2dd54fa5fef0375c709d67b1c78d0 (patch)
tree0a3f8592ecf8e364fec7a3ef4dad55329c394d1d /docs/superpowers/followup/pr3840-write-report-atomicity.md
parentd0b76fe8b91408d22e86b731b72a9a19a9676b7c (diff)
docs: design, plan and follow-ups for the hil_report consolidationhil-report
Records why the report code was spread across three modules and what the consolidation buys, so the next reader does not have to re-derive it from the diff. Names the new functions explicitly rather than presenting the change as pure code motion -- that framing points reviewers away from the code that carried the defects. Three findings this PR deliberately does not close get one handoff each, per CLAUDE.md: the worker-result tuple hil_report still unpacks positionally, SKILL.md's no-boards rule drifting from the code, and write_report's two non-atomic writes. Drops the pr3836 handoff, which this branch implements.
Diffstat (limited to 'docs/superpowers/followup/pr3840-write-report-atomicity.md')
-rw-r--r--docs/superpowers/followup/pr3840-write-report-atomicity.md30
1 files changed, 30 insertions, 0 deletions
diff --git a/docs/superpowers/followup/pr3840-write-report-atomicity.md b/docs/superpowers/followup/pr3840-write-report-atomicity.md
new file mode 100644
index 000000000..2094207bb
--- /dev/null
+++ b/docs/superpowers/followup/pr3840-write-report-atomicity.md
@@ -0,0 +1,30 @@
+# `write_report` commits the two artifacts non-atomically
+
+**Origin:** split out of PR #3840, surfaced by its second review round. Delete this file
+when its own PR lands.
+
+```python
+md = render_report(doc) + '\n'
+report_dir.mkdir(parents=True, exist_ok=True)
+(report_dir / REPORT_JSON).write_text(json.dumps(doc, indent=2) + '\n')
+(report_dir / REPORT_MD).write_text(md, encoding='utf-8')
+```
+
+Rendering before writing closed the *render-failure* case: a raise can no longer commit a
+sidecar the markdown contradicts. It does not close the *interrupted-between-writes* case. A
+kill between those two lines leaves the pair disagreeing — and this runs on the containment
+path, on the way to `os._exit`, on a rig whose jobs get cancelled by the GitHub job ceiling.
+
+**What remains:** write both to temp files, then `os.replace` both. The window shrinks from
+two full writes to two renames, and neither file is ever observed half-written. `os.replace`
+is atomic per file on POSIX; the pair is still not transactional, which is acceptable and
+should be said in the docstring rather than implied away.
+
+Worth pairing with a test that kills between the writes — or, more practically, one that
+asserts no partial file is ever visible by checking the temp-then-rename shape directly.
+
+## Why it was split out
+
+A durability edge, not a wrong verdict. PR #3840 closed the render-failure half of this
+(nothing is written until the markdown renders); the interrupted-between-writes half needs
+a temp-then-rename and is better reviewed on its own.