refactor(jsonutil): centralize streaming atomic replacement - #2053
Open
MuskanPaliwal wants to merge 4 commits into
Open
refactor(jsonutil): centralize streaming atomic replacement#2053MuskanPaliwal wants to merge 4 commits into
jsonutil): centralize streaming atomic replacement#2053MuskanPaliwal wants to merge 4 commits into
Conversation
MuskanPaliwal
marked this pull request as ready for review
August 19, 2026 10:49
jsonutil): centralize streaming atomic replacement
MuskanPaliwal
force-pushed
the
refactor-streaming-atomic-write
branch
from
August 24, 2026 12:04
7c62491 to
ffdb142
Compare
Contributor
Author
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Depends on #1877. Until that PR merges, review the stacked range
3a39273b..7c62491a5.This change moves OpenCode’s staged transcript publication into
jsonutil.WriteFileAtomicStream. The previous implementation kept temporary-file creation, validation, rename retries, and recovery inside OpenCode even though most of that lifecycle is general atomic-write behavior. The shared implementation keeps publication ordered as produce, sync, close, validate, chmod, rename, and best-effort directory sync.Core Changes
WriteFileAtomicStreamnow owns the staging file from creation through publication. Producer and validator errors are returned unchanged. Any failure before publication leaves the existing destination untouched and makes a best-effort attempt to remove the staging file. If a completed and validated file cannot be renamed into place, the helper returns aPublishErrorand transfers ownership of the retained staging path to the caller so the output can be recovered.Windows replacement contention is handled inside
jsonutilrather than OpenCode. Only access-denied and sharing-violation failures are retried, with five total attempts and cancellation-aware waits. The follow-up review found a cancellation window between retries: cancellation could occur after a wait, but the next rename could still replace the destination. The retry loop now checks the context before every rename attempt, retaining the validated staging file if cancellation wins.OpenCode continues to translate publication failures into its existing actionable “export saved at …” message. Its package-specific staging, rename, and contention helpers have been removed because those responsibilities now belong to the shared atomic-write implementation.
API Notes
This adds the public
jsonutil.WriteFileAtomicStreamhelper and the publicjsonutil.PublishErrorownership contract. A caller receivingPublishErrorowns the validated file atStagedPathand must either recover or remove it.The existing
jsonutil.WriteFileAtomicfunction uses the same private engine but keeps its previous behavior. Rename failures do not exposePublishError, and its staging files are still cleaned up instead of being retained.Compatibility / Release Notes
OpenCode command invocation, timeout and error classification, mock export mode, transcript validation, attach behavior, transcript protocol, and declared capabilities are unchanged. Failed, partial, empty, invalid, and cancelled exports cannot replace an existing valid transcript.
No unrelated temporary-file or rename-based writers are migrated in this change because they may have different locking, permission, confinement, executable, or cross-filesystem requirements.
This branch is stacked on #1877 and should be rebased onto
mainafter that PR merges.Testing
mise run checkjsonutiland OpenCode