Repository navigation
feat(OMN-20392): add replace_in_files multi-file edit action to the delegated code edit node - #3270
Merged
Merged
Conversation
✅ Architectural Review — PASSEDErrors: 0 What this checks
No architectural violations found. Static architectural lint — no model inference (OMN-14176). |
✅ Hostile Reviewer — PASSEDBlocking findings (critical/error): 0 Gate semantics
Powered by omniintelligence.review_pairing.cli_review — node-based adversarial review via HandlerLlmCliSubprocess (OMN-8524) |
The delegated code edit node allowed at most 12 actions per turn and had no multi-file action, so a 105-file edit needed at least 9 turns of single edits. Add one typed action, replace_in_files, declared in the node contract, the tool schemas and the response contract. It takes file_paths (list, at most 200) or glob (exactly one), plus old_string and new_string, and replaces every occurrence of old_string in each file. Each file is all-or-nothing: computed in memory and written once. A named file without old_string, outside the writable globs, or whose write is refused is reported as FAILED and left untouched. A glob covers only writable manifest files and skips files without old_string. The result lists failures first, then every edited file with its occurrence count, then is capped. There is no shell and no script; the same writable globs and confinement apply. In the tool_use rubric, replace_in_files is an edit tool (edits_apply) and file_paths is a path argument (no_phantom_paths now reads lists). One call is recorded per action, failed when any file failed, so within_budget's 80-call cap still holds. Also, loop_ports no longer crashes with AttributeError when a failed delegate receipt carries terminal_payload null; it becomes a failed turn reply. Tests: handler (30-file one-action edit, glob scope, partial failure, per-file write error, caps, ordering), parser, rubric, loop_ports.
_cf_cost built the premium counterfactual with the default baseline model, which returned None and failed collection of the module.
jonahgabriel
force-pushed
the
OMN-20392-code-edit-bulk-replace
branch
from
October 3, 2026 04:04
e8642a5 to
dc9a05c
Compare
Add contracts/OMN-20392.yaml with two acceptance criteria bound to replace_in_files handler tests and tool-use rubric tests. This resolves repo-evidence / dod-verify failure where the PR cites OMN-20392 but carries no contracts/OMN-20392.yaml. Revert dc9a05c (pin of premium model in tests/test_omn20303_real_postgres_savings_one_row_per_run.py). The canonical handler path uses the default baseline model claude-sonnet-5-5, so CI saw savings 0.002178 against the pinned claude-opus-4-6 expectation 0.016335 in Integration Silent-Skip Guard. Lab h201: default counterfactual for 319/154 tokens is 0.002178; focused tests 141 passed, 2 skipped. Refs: OMN-20392, PR 3270
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.
Ticket: OMN-20392
Lab: host=h201 lane=codeedit-bulk-2a21 head=7dc39e3cd7d983693836b027742a0ba46c3d630a command=in-process code-edit loop (real handler, worktree, checks; Qwen3.8-27B on the h201 model server) observed=loop e58a5fbb-afa0-4c8c-aafe-44615e78b96a accepted, 110 files changed in 4 turns, rubric PASS
Summary
The delegated code edit node allowed at most 12 actions per turn and had no multi-file action, so the crush replay's 105-file and 114-file tasks (f76301f0, 8e1b5f72) needed at least 9 and 10 turns of single edits. This blocks cutover parity (OMN-20291, OMN-20292).
This change adds one typed action,
replace_in_files, which replaces every occurrence ofold_stringwithnew_stringacross many files in a single action.Design
One typed action
replace_in_files, declared in the node contract description, TOOL_SCHEMAS and RESPONSE_CONTRACT.file_paths(array, at most MAX_BULK_FILES=200) orglob(same syntax as writable globs, expanded over the worktree manifest); plusold_string(required) andnew_string(may be empty).old_stringin each file.Per-file all-or-nothing — content is computed in memory and written once. A named file that is outside the worktree, outside the writable globs, lacks
old_string, would exceed MAX_WRITE_BYTES, or whose write is refused is reportedFAILED <path>: <reason>and left untouched. Refusals count into the result's refusals.Glob is a scope — only writable manifest files are targeted; files without
old_stringare skipped and counted; files matching the glob but outside the writable globs are counted; a glob that edits nothing is an error.Feedback to the model lists failures first, then each edited file with its occurrence count, capped at MAX_OBSERVATION_BYTES — so a cap cuts successes only.
No shell, no generated script — only this typed action, same confinement and writable globs. The 12-actions-per-turn cap is unchanged.
Rubric:
replace_in_filesadded toedits_applyedit_tools.file_pathsadded tono_phantom_pathspath_argument_names;no_phantom_pathsnow reads list-valued path arguments.errorwhen any file failed), becausewithin_budgetcaps tool calls at 80 — per-file calls would break it for a 105-file edit.Also fixed:
DelegatedCodeEditPorts.delegateraised AttributeError on a failed delegate receipt whoseterminal_payloadis null; it is now a failed turn reply (test added).Tests
Focused run of
tests/nodes/node_delegated_code_edit_orchestrator,tests/unit/delegated_code_editandtests/nodes/node_delegation_rubric_check_compute: 382 passed.ruff checkand format clean,mypy --strictonsrc/omnimarketclean, runtime sweep import check and metadata dependency check pass.New tests cover:
edits_applyfails)edits_applyandno_phantom_pathsonreplace_in_filesLab proof
Real Qwen3.8-27B on the h201 lab model server, the real loop handler, worktree and checks, in-process:
f477e734-4b9a-49f1-8c73-a1d6562d1304— 41-file fixture (40 files to migrate): status accepted in 3 turns, onereplace_in_filesaction by glob, 40 files changed, check passed, rubric outcome PASS.e58a5fbb-afa0-4c8c-aafe-44615e78b96a— 110-file fixture across two directories: status accepted in 4 turns, 110 files changed, check passed, rubric outcome PASS.14ed37af-fe89-4149-bc35-a7789e3bf5f6,baaeb8b9-9d51-46b9-8397-388a6e525a01,4f5ddbcd-b9c7-4dd5-8726-56463f9e5131(in the lane scratch state root).Notes
Limit of the proof:
onex code-edit runitself could not reach a model from h201 today. The dev lane failed with "delegate receipt carries no resolvable delegation terminal" (loopf86b9d8f-33a2-4a20-b14e-f1ab7562a4ab), and the in-process route failed with the DelegatePhaseTimedBus.subscribe TypeError (delegate run141fdc8d-13eb-4c0c-a6e2-dba8ef6a7d8e, loop1974e6bc-4140-4dc0-a7a1-a1506347d1bd). The lab runs therefore replaced only theonex delegatehop with a direct call to the model server; the prompt, response parsing, apply, checks, receipt and rubric score are the shipped code.Follow-up: re-running through
onex code-edit runon the dev lane once the delegate route is back.Not covered: the crush replay's actual 105-file and 114-file tasks were not replayed.
Evidence-Ticket: OMN-20392
Evidence-Source: OCC#12449