You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Targeted: 0.5.0. Baseline: release/0.3.0. This is the main feature of the reliability release. All design decisions are now resolved (maintainer, 2026-08-13); the previously open ones are recorded at the end.
Shape. A structured sibling to apply_patch, sharing the staging/commit kernel (patching.py's FileBaseline + atomic multi-file committer):
Ops: create / write / edit / delete / move / copy. write is an upsert (revision required when the target exists) and replaces the replace_all-as-edit special case; create asserts non-existence and takes no revision.
edit uses whole-line semantics: content is split on \n and replaces [start_line, end_line]; content: "" is special-cased to zero lines (range deletion); both insert_after (0..total) and insert_before (1..total+1) exist, boundary values documented in the schema.
revision = full-file sha256, required for edit/delete/move/copy. Escape hatch is a server start flag (--allow-unversioned-changes), never a wire value. apply_patch does not gain revision: hunk context is content-level stale protection and better at it; the two entrances share result shape (affected_files with previous_revision/revision) but not the protection mechanism — documented explicitly.
read_file computes the hash from the same buffer it decodes (removes the TOCTOU between hash and content; also removes the separate binary-sniff open), returns revision in payload and in the model-visible line view. MAX_REVISION_BYTES ~8 MB; oversized files get revision: null.
Committer: StagedFile gains explicit action: write | delete | verify (copy must validate its source baseline without writing it), replacing the implicit content is None encoding.
Same-path handling (amended after field evidence). The planner is strictly declarative; true chaining (an op reading another op's intermediate state) is removed. But the apply_patch frontend gains a stateless duplicate-Update normalizer: multiple *** Update File: blocks for one resolved path are concatenated into a single block before planning. The engine already anchors every hunk against the original content and rejects overlaps, so the merged block gets exactly the declarative semantics for free. Any same-path duplication involving Add/Delete, or two blocks with contradictory *** Move to:, is rejected with PATCH_PATH_CONFLICT; a block written against another block's intermediate state simply fails to anchor and reports PATCH_CONTEXT_NOT_FOUND (indistinguishable from stale context, same recovery). Normalizations are counted in telemetry. apply_changes itself stays strict: one path per call.
Evidence: OpenAI-family models routinely emit independent duplicate Update blocks (apply_patch rejects multiple update blocks for the same file, which openai models try to do a lot microsoft/vscode#329812, ~daily); Codex executed Delete → Add chains whose merged report hid the delete — an audit hole (apply_patch can delete without visible Guardian review and report Delete+Add as Add-only openai/codex#34515); Aider forbids duplicates in its prompt but merges them in its parser; gpt-oss's reference parser rejects them outright. Tolerant-but-principled beats both extremes.
Limits: 100 changes × 100 edits as sanity caps; the real bound is MAX_HTTP_REQUEST_BYTES (1 MiB → HTTP 413), documented. Empty changes behaves like apply_patch's "No files were modified". Path validation (reject_write_symlink + resolve_for_write) runs in the planner before staging, and same-path identity is judged on resolved paths.
Targeted: 0.5.0. Baseline:
release/0.3.0. This is the main feature of the reliability release. All design decisions are now resolved (maintainer, 2026-08-13); the previously open ones are recorded at the end.Shape. A structured sibling to
apply_patch, sharing the staging/commit kernel (patching.py'sFileBaseline+ atomic multi-file committer):create/write/edit/delete/move/copy.writeis an upsert (revision required when the target exists) and replaces thereplace_all-as-edit special case;createasserts non-existence and takes no revision.edituses whole-line semantics:contentis split on\nand replaces[start_line, end_line];content: ""is special-cased to zero lines (range deletion); bothinsert_after(0..total) andinsert_before(1..total+1) exist, boundary values documented in the schema.revision= full-file sha256, required foredit/delete/move/copy. Escape hatch is a server start flag (--allow-unversioned-changes), never a wire value.apply_patchdoes not gain revision: hunk context is content-level stale protection and better at it; the two entrances share result shape (affected_fileswithprevious_revision/revision) but not the protection mechanism — documented explicitly.read_filecomputes the hash from the same buffer it decodes (removes the TOCTOU between hash and content; also removes the separate binary-sniff open), returnsrevisionin payload and in the model-visible line view.MAX_REVISION_BYTES~8 MB; oversized files getrevision: null.StagedFilegains explicitaction: write | delete | verify(copymust validate its source baseline without writing it), replacing the implicitcontent is Noneencoding.apply_patchfrontend gains a stateless duplicate-Update normalizer: multiple*** Update File:blocks for one resolved path are concatenated into a single block before planning. The engine already anchors every hunk against the original content and rejects overlaps, so the merged block gets exactly the declarative semantics for free. Any same-path duplication involvingAdd/Delete, or two blocks with contradictory*** Move to:, is rejected withPATCH_PATH_CONFLICT; a block written against another block's intermediate state simply fails to anchor and reportsPATCH_CONTEXT_NOT_FOUND(indistinguishable from stale context, same recovery). Normalizations are counted in telemetry.apply_changesitself stays strict: one path per call.Evidence: OpenAI-family models routinely emit independent duplicate Update blocks (apply_patch rejects multiple update blocks for the same file, which openai models try to do a lot microsoft/vscode#329812, ~daily); Codex executed
Delete → Addchains whose merged report hid the delete — an audit hole (apply_patchcan delete without visible Guardian review and report Delete+Add as Add-only openai/codex#34515); Aider forbids duplicates in its prompt but merges them in its parser;gpt-oss's reference parser rejects them outright. Tolerant-but-principled beats both extremes.MAX_HTTP_REQUEST_BYTES(1 MiB → HTTP 413), documented. Emptychangesbehaves likeapply_patch's "No files were modified". Path validation (reject_write_symlink+resolve_for_write) runs in the planner before staging, and same-path identity is judged on resolved paths.REQUIRED_TOOLS, schema-drift, contract, READMEs all move together.Resolved decisions:
revisionrequired for edit/delete/move/copy (create takes none); escape hatch is a server start flag only.apply_patchfrontend as specified above.