atomicWrite and trace appends have predictable temp names and check/open races #152

Closed
opened 2026-08-15 14:33:49 +00:00 by john · 0 comments
Owner

Source: deep dive F12; strict re-review C-18
Verdict: DISPUTE - overstated, closing

Claim as reviewed

atomicWrite computes a predictable sibling temp name, checks it with lstatSync, then opens it separately, so a path can change between check and open and an existing hard link is followed. The function provides no lock, no exclusive create and no fsync. Trace and read-ledger appenders have the same shape.

At HEAD f354864 (v3.3.0)

Every mechanical fact is accurate, and every one of them is already stated in the source.

cadence-core/bin/lib/planning-files.mjs:2059-2078 - the temp name is <file>.<pid>.<seq>.tmp, unique per call via a module counter, not a fixed sibling. :2028-2039 states the full residual: "There is no lock, no O_EXCL retry and no fsync here - the promise is only that a crash never leaves a torn file", plus the deliberate last-writer-wins behaviour (D-05) and the deliberate overwrite of an ordinary stale temp.

cadence-core/bin/lib/trace.mjs:268-297 - same lstatSync-then-appendFileSync shape, with the reasoning stated at :268-270.

Note

Winning the check/open window requires write access to .planning/ - the same access someone would use to edit ROADMAP.md directly, which needs no race at all. The stated promise (a crash never leaves a torn file) is met.

Closed as an accepted, documented cost rather than a defect. If durability of .planning state ever becomes load-bearing, an fsync-before-rename is the change worth revisiting.

**Source:** deep dive F12; strict re-review C-18 **Verdict:** DISPUTE - overstated, closing ### Claim as reviewed `atomicWrite` computes a predictable sibling temp name, checks it with `lstatSync`, then opens it separately, so a path can change between check and open and an existing hard link is followed. The function provides no lock, no exclusive create and no fsync. Trace and read-ledger appenders have the same shape. ### At HEAD f354864 (v3.3.0) Every mechanical fact is accurate, and every one of them is already stated in the source. `cadence-core/bin/lib/planning-files.mjs:2059-2078` - the temp name is `<file>.<pid>.<seq>.tmp`, unique per call via a module counter, not a fixed sibling. `:2028-2039` states the full residual: "There is no lock, no `O_EXCL` retry and no `fsync` here - the promise is only that a crash never leaves a torn file", plus the deliberate last-writer-wins behaviour (D-05) and the deliberate overwrite of an ordinary stale temp. `cadence-core/bin/lib/trace.mjs:268-297` - same `lstatSync`-then-`appendFileSync` shape, with the reasoning stated at `:268-270`. ### Note Winning the check/open window requires write access to `.planning/` - the same access someone would use to edit `ROADMAP.md` directly, which needs no race at all. The stated promise (a crash never leaves a torn file) is met. Closed as an accepted, documented cost rather than a defect. If durability of `.planning` state ever becomes load-bearing, an fsync-before-rename is the change worth revisiting.
john 2026-08-15 14:33:49 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: crenshawdev/cadence-archived#152
No description provided.