Skip to content

fix(sdk): fs adapter flush() tracks in-flight writes; add to T13 contract suite#1425

Merged
vanceingalls merged 2 commits into
mainfrom
06-13-fix_sdk_fs_adapter_flush_tracks_in-flight_writes_add_to_t13_contract_suite
Jun 15, 2026
Merged

fix(sdk): fs adapter flush() tracks in-flight writes; add to T13 contract suite#1425
vanceingalls merged 2 commits into
mainfrom
06-13-fix_sdk_fs_adapter_flush_tracks_in-flight_writes_add_to_t13_contract_suite

Conversation

@vanceingalls

Copy link
Copy Markdown
Collaborator

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?

  • Unit tests added/updated
  • Manual testing performed
  • Documentation updated (if applicable)

@james-russo-rames-d-jusso james-russo-rames-d-jusso left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.all rejects on the FIRST in-flight rejection, which means if write A rejects and B is still pending, B's eventual error never surfaces through flush(). 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 consider Promise.allSettled + aggregate. Not blocking — the errorHandlers callback already fires per-write inside doWrite'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 runPersistAdapterContract runs across both, fine; worth a quick rg "flush.*reject" packages/sdk/src/adapters/persistAdapter.contract.test.ts to 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
miguel-heygen previously approved these changes Jun 15, 2026

@miguel-heygen miguel-heygen left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 a Set<Promise<void>> that removes itself on settle is the correct pattern; flush() correctly snapshots the current set via Promise.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.ts flush() — there is no timeout or cancellation path. If an in-flight write() 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

@vanceingalls vanceingalls force-pushed the 06-13-fix_sdk_fs_adapter_flush_tracks_in-flight_writes_add_to_t13_contract_suite branch from d22af04 to a531d34 Compare June 15, 2026 05:06
@vanceingalls vanceingalls force-pushed the 06-13-feat_sdk_playground_fs_adapter_settiming_gsap_sync branch from 1604228 to def5462 Compare June 15, 2026 05:06
@vanceingalls vanceingalls force-pushed the 06-13-fix_sdk_fs_adapter_flush_tracks_in-flight_writes_add_to_t13_contract_suite branch from a531d34 to c80335d Compare June 15, 2026 08:13
@vanceingalls

Copy link
Copy Markdown
Collaborator Author

@james-russo-rames-d-jusso — flush() semantics documented

Added a one-line comment to flush() clarifying that Promise.all rejects on first write failure, and that per-write errors are independently surfaced via the persist:error event channel. First-error semantics are intentional — callers who need all-errors should subscribe to persist:error directly.

No change to the Promise.allSettled alternative for now; the event channel provides complete observability.

Base automatically changed from 06-13-feat_sdk_playground_fs_adapter_settiming_gsap_sync to graphite-base/1423 June 15, 2026 08:23
@vanceingalls vanceingalls force-pushed the 06-13-fix_sdk_fs_adapter_flush_tracks_in-flight_writes_add_to_t13_contract_suite branch from c80335d to 0df98a6 Compare June 15, 2026 08:26
vanceingalls and others added 2 commits June 15, 2026 01:56
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>
@vanceingalls vanceingalls force-pushed the 06-13-fix_sdk_fs_adapter_flush_tracks_in-flight_writes_add_to_t13_contract_suite branch from 0df98a6 to 59793ee Compare June 15, 2026 08:56
@vanceingalls vanceingalls changed the base branch from graphite-base/1423 to main June 15, 2026 08:57
@vanceingalls vanceingalls dismissed miguel-heygen’s stale review June 15, 2026 08:57

The base branch was changed.

@vanceingalls vanceingalls merged commit 0a30011 into main Jun 15, 2026
16 checks passed
@vanceingalls vanceingalls deleted the 06-13-fix_sdk_fs_adapter_flush_tracks_in-flight_writes_add_to_t13_contract_suite branch June 15, 2026 08:57
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.

3 participants