fix(sdk): fs adapter flush() tracks in-flight writes; add to T13 contract suite#1425
Conversation
james-russo-rames-d-jusso
left a comment
There was a problem hiding this comment.
Verified at HEAD d22af04.
Tight, correct fix. The inflightWrites: Set<Promise<void>> pattern is the right shape — doWrite is wrapped so the promise is registered BEFORE await, then removed in finally, which means flush() sees every in-flight write whether settling, mid-write, or rejected. Promise.all([...inflightWrites]) snapshots the set at flush-time, so writes that arrive AFTER the flush call don't extend it — that matches typical adapter contract semantics.
The bonus versionCounter monotonic suffix on the appendVersion key is the right call — Date.now() collisions under fast sequential writes would have silently dropped versions when the lexicographic-sort prune ran. Padded to 6 digits → 1M writes/ms headroom, plenty.
T13 contract suite running against mkdtempSync(tmpdir()) per-test is clean: real fs, isolated, no cross-test pollution.
Concerns
flush()rejection semantics:Promise.allrejects on the FIRST in-flight rejection, which means if write A rejects and B is still pending, B's eventual error never surfaces throughflush(). Cross-check with the T13 contract — does the spec require flush to surface ALL errors or just the first? If first-error-only is intentional, a one-line comment would help; otherwise considerPromise.allSettled+ aggregate. Not blocking — theerrorHandlerscallback already fires per-write insidedoWrite's catch in the prior shape (this PR's diff doesn't touch that path), so observability is preserved.- Lens item (c) "error during write reflected in flush rejection" — the T13 diff in this PR (+9 lines) doesn't appear to add an explicit error-during-write-then-flush case. If the existing suite already covers it on the memory adapter and the
runPersistAdapterContractruns across both, fine; worth a quickrg "flush.*reject" packages/sdk/src/adapters/persistAdapter.contract.test.tsto confirm.
Determinism: no Date.now() / Math.random() / fetch() in render-time paths. Date.now() is adapter-write (author-time) — fine.
Verdict: approve-with-concerns (none blocking; first-error semantics worth a comment or follow-up).
Review by Rames D Jusso
miguel-heygen
left a comment
There was a problem hiding this comment.
CI is blocked by the oxfmt failure in #1423 (root of the stack). The code change itself is clean.
Strengths:
packages/sdk/src/adapters/fs.ts— tracking in-flight writes as aSet<Promise<void>>that removes itself on settle is the correct pattern;flush()correctly snapshots the current set viaPromise.all([...this.inflightWrites])so concurrent writes started during the await don't defer the caller's flush boundary.packages/sdk/src/adapters/fs.ts:versionCounter—${Date.now()}_${String(counter).padStart(6,"0")}guarantees lexicographic = insertion order within the same millisecond; version stability fix is correct.- Adding the inflight tracking to the T13 contract suite is good — contracts are the right place to pin this.
Important:
packages/sdk/src/adapters/fs.tsflush()— there is no timeout or cancellation path. If an in-flightwrite()never resolves (e.g. underlying fs.promises.writeFile hangs),flush()blocks forever. Low-urgency for a Node-only adapter, but worth a follow-up.
Verdict: APPROVE (pending #1423 oxfmt fix)
Reasoning: Correct fix, well-tested. Unblocked as soon as the root format failure in #1423 is resolved.
— magi
d22af04 to
a531d34
Compare
1604228 to
def5462
Compare
a531d34 to
c80335d
Compare
|
@james-russo-rames-d-jusso — flush() semantics documented Added a one-line comment to No change to the |
c80335d to
0df98a6
Compare
d57600d to
4da25d6
Compare
Promise.all rejects on first write failure; errors also surface via persist:error event channel per write. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> Co-authored-by: Miguel Ángel <miguel07alm@protonmail.com>
0df98a6 to
59793ee
Compare
The base branch was changed.

What
Brief description of the change.
Why
Why is this change needed?
How
How was this implemented? Any notable design decisions?
Test plan
How was this tested?