痛点与背景 / Problem and Context
Yada currently exposes a single mutation tool: apply_patch.
The existing implementation has valuable safety properties:
- every existing target is bound to the SHA-256 returned by
read_file;
expected_files must exactly match the files touched by the patch;
git apply --check validates the complete transaction before mutation;
- a failed patch does not partially modify the workspace;
- every successful edit invalidates previous verification;
finish remains blocked until a relevant test or build succeeds after the latest edit.
These guarantees should be preserved.
However, the model must currently solve two different problems at once:
- decide what source code should change;
- encode that change as a mechanically valid unified diff, including hunk headers, line counts, prefixes, and exact context.
This creates a reliability bottleneck. A logically correct change can be rejected because the model miscounted hunk lines or produced slightly incorrect context. The failure is safe and visible, but it consumes additional model turns and can prevent otherwise valid tasks from completing.
The current failure observation is also too coarse:
{
"ok": false,
"error": "git apply check failed: ..."
}
Although this observation is returned to the next model turn, it does not distinguish stale files, incorrect hunk metadata, missing source text, ambiguous locations, unsupported targets, or internal application failures. We want to improve edit reliability without weakening Yada's core principles:
- zero runtime dependencies;
- a small and stable tool interface;
- explicit SHA-based optimistic locking;
- deterministic and auditable mutations;
- fail-closed behavior;
- bounded structured observations;
- no fuzzy guessing about edit locations;
- mandatory verification after every successful mutation.
期望的解决方案 / Proposed Solution
Implement a layered editing strategy:
localized edit to existing text
↓
replace_text
├── success → edit complete
├── recoverable failure → return structured evidence
│ → re-read in the next Agent turn
│ → retry replace_text or call apply_patch
└── unsupported operation → use apply_patch
new file / deleted file / unsuitable large structural edit
↓
apply_patch
The fallback must happen through the normal Agent loop after the model receives the failure observation. It must not silently apply a more permissive mutation inside the failed replace_text call.
Implementation plan
Recommended dependency order:
#8 apply_patch hardening
↓
#9 replace_text
↓
#10 routing and evaluation
#8 can be implemented and released independently. #10 depends on both mutation tools and their structured failure observations.
Shared safety invariants
Both mutation paths must preserve the following behavior:
- all existing files are bound to their current SHA-256;
- paths must remain inside the workspace;
- protected paths and unsupported targets are rejected;
- all requested changes are validated before any file is mutated;
- multi-file edits are atomic from Yada's perspective;
- ambiguous edit locations are rejected instead of guessed;
- failures return bounded structured evidence;
- successful edits update the run revision and touched-file state;
- every successful mutation invalidates previous verification;
finish remains blocked until verification succeeds after the latest edit;
- traces contain enough information to audit the selected tool, result, and recovery path.
Error contract
Editing tools should return stable machine-readable error codes while retaining a concise human-readable message.
Expected codes include:
stale_hash
no_match
ambiguous_match
invalid_edit
invalid_patch
unsupported_target
patch_context_mismatch
apply_failed
Error details must be bounded. A failure must not automatically echo an entire large file into the observation.
Completion criteria
This tracking issue is complete when:
- all three child issues are closed;
patch-only editing remains available;
- replace-first editing can be selected and audited per run;
- neither path weakens the existing SHA, transaction, or verification guarantees;
- no runtime dependency is added;
- unit and integration tests cover success, recoverable failure, rollback, and verification invalidation;
- traces record the run-level editing strategy and mutation results;
- benchmark results compare
patch-only and replace-first using the same task set and model configuration.
Out of scope
The following are intentionally excluded from this milestone:
patch --fuzz or whitespace-insensitive matching;
- partial patch application or
.rej files;
- implicit three-way merging;
- silently choosing a “similar” location;
- AST-specific editing;
- whole-file rewrite as a separate public tool;
- binary-file editing;
- external Fast Apply models or services;
- automatic fallback inside a single failed tool invocation.
痛点与背景 / Problem and Context
Yada currently exposes a single mutation tool:
apply_patch.The existing implementation has valuable safety properties:
read_file;expected_filesmust exactly match the files touched by the patch;git apply --checkvalidates the complete transaction before mutation;finishremains blocked until a relevant test or build succeeds after the latest edit.These guarantees should be preserved.
However, the model must currently solve two different problems at once:
This creates a reliability bottleneck. A logically correct change can be rejected because the model miscounted hunk lines or produced slightly incorrect context. The failure is safe and visible, but it consumes additional model turns and can prevent otherwise valid tasks from completing.
The current failure observation is also too coarse:
{ "ok": false, "error": "git apply check failed: ..." }Although this observation is returned to the next model turn, it does not distinguish stale files, incorrect hunk metadata, missing source text, ambiguous locations, unsupported targets, or internal application failures. We want to improve edit reliability without weakening Yada's core principles:
期望的解决方案 / Proposed Solution
Implement a layered editing strategy:
The fallback must happen through the normal Agent loop after the model receives the failure observation. It must not silently apply a more permissive mutation inside the failed
replace_textcall.Implementation plan
apply_patchwith--recountand structured edit errorsreplace_textRecommended dependency order:
#8can be implemented and released independently.#10depends on both mutation tools and their structured failure observations.Shared safety invariants
Both mutation paths must preserve the following behavior:
finishremains blocked until verification succeeds after the latest edit;Error contract
Editing tools should return stable machine-readable error codes while retaining a concise human-readable message.
Expected codes include:
stale_hashno_matchambiguous_matchinvalid_editinvalid_patchunsupported_targetpatch_context_mismatchapply_failedError details must be bounded. A failure must not automatically echo an entire large file into the observation.
Completion criteria
This tracking issue is complete when:
patch-onlyediting remains available;patch-onlyandreplace-firstusing the same task set and model configuration.Out of scope
The following are intentionally excluded from this milestone:
patch --fuzzor whitespace-insensitive matching;.rejfiles;