Skip to content

🎯 feat: Diagnose Every Failing Workspace Edit and Negotiate Tolerant Matching - #271

Merged
danny-avila merged 10 commits into
mainfrom
danny-avila/edit-matching
Sep 30, 2026
Merged

danny-avila merged 10 commits into
mainfrom
danny-avila/edit-matching

Conversation

@danny-avila

Copy link
Copy Markdown
Collaborator

Summary

Agents using a paired worker's edit_file fail often, and each failure costs them extra turns. Over 10 days on one deployment, 125 of 2,935 edit_file calls (4.3%) failed with EDIT_CONFLICT. Every one got the same message: Workspace edit must match exactly once. That message does not say whether the text was missing or repeated, which edit in the batch failed, or where the intended text is. 94 of the 125 failures were multi-edit batches (2–14 edits), where one bad edit rejects the whole call. The agent's usual recovery was a blind retry (35) or a full re-read (30).

This PR changes the worker's edit engine in two ways.

Every failing edit is diagnosed, in all modes, with no negotiation. Edits still apply in order and still commit atomically. When one fails, the worker keeps checking the rest and rejects the batch with a single EDIT_CONFLICT. Its message lists every failing edit by position:

  • Missing edits name the nearest candidate line. They also flag the usual causes: an elision (...) in oldText, copied line-number prefixes, a whitespace-only difference (with the line where the text does exist), or CRLF line endings.
  • Ambiguous edits give their match count and line numbers. As before, overlapping occurrences count as separate locations.

The message stays under 3,000 characters to fit the 4,096-character settlement bound, and it travels through the existing error path unchanged.

2 of 3 workspace edits did not apply, so nothing was written. Every other edit matched.
Edit 2: old_text matched 2 locations at lines 2, 3; include more surrounding lines so it matches exactly one.
Edit 3: old_text was not found; the closest line is line 9: "const total = price * quantity;".
Line numbers account for the earlier edits in this batch.

Two negotiated edit features use the existing editFileFeatures handshake. The worker advertises them, the Code API lists them in supportedWorkspaceEditFileFeatures, and it only dispatches requests that use them to workers that negotiated them:

  • tolerant_match: a request-level matching: 'tolerant'. When an exact match fails, it falls back in order to:

    • line-trimmed: ignores trailing whitespace and CRLF;
    • indentation-flexible: matches a uniformly shifted block, and moves newText to the file's own indentation;
    • whitespace-normalized: any whitespace run between tokens, and strips the leading and trailing whitespace the caller wrapped around both texts.

    A match must still be unique, and replacements keep the file's line endings.

  • replace_all: a batch edit's replaceAll: true replaces every non-overlapping match. It still fails when nothing matches.

A request that sets matching or any replaceAll gets matches: one { strategy, occurrences } per edit. A request that sets neither gets exactly the legacy result, so older callers and validators never see a new key.

The tolerant tiers follow LibreChat's existing skill-file matcher, but fix three problems found while porting it:

  • Tier order: whitespace-normalized now runs last, so an indentation-only difference is handled by the tier that re-indents newText. Before, it inserted newText as written.
  • CRLF: line-window matches keep CRLF on the last matched line and in the replacement.
  • Trailing newline: an oldText ending in a newline matches through the line terminator, instead of requiring an extra blank line.

Related to #267. LibreChat will opt into tolerant_match and replace_all in a separate PR, and render these diagnostics.

How it works

applyWorkspaceEdits (edit_file, preview_edit)
  applyTextEdits(text, edits, matching)          packages/code/src/edits.ts
    for each edit, against the text so far:
      exact                                        always
      line-trimmed -> indentation-flexible -> whitespace-normalized   when matching: 'tolerant'
      unique match, or every match with replaceAll -> apply
      otherwise record { index, reason }, keep going
    any failures -> WorkspaceEditMatchError -> WorkspaceToolError('EDIT_CONFLICT')

Negotiation and gating:

  • WORKSPACE_EDIT_FILE_FEATURES in protocol.ts is the single list the worker advertises, the Code API negotiates and capability validation accepts, as any unique subset.
  • supportsWorkspaceTool in bridge/store.ts refuses matching or replaceAll for a worker that did not negotiate the matching feature, the same way it gates expected_base_sha256.
  • Result validation requires matches exactly when the request opted in, with exact strategies only for non-tolerant requests and occurrences > 1 only for replaceAll edits.

Testing

  • packages/code: new edits.test.ts (17 cases, covering diagnostics, tolerant tiers, CRLF, re-indentation, replaceAll, overlap, the output bound and large-file performance); worker integration tests in workspace.test.ts through LocalWorkspaceTools; protocol tests for request, result and capability validation. npm test: 564 pass. The 9 failures (PTC watchdog and credential storage tests) fail identically on unmodified main in the same WSL environment.
  • service: bun test ./src/bridge: 196 pass, 0 fail, including new gates for tolerant edits, tolerant previews and replaceAll. tsc --noEmit reports the same 7 existing errors as main and no new ones.
  • service bun run build fails the same way on main locally (Node 18 loading the rollup config), so CI's build is the check there.

…Matching

A rejected edit batch now reports every failing edit by position: missing
edits name the nearest candidate line and flag elided, line-numbered,
whitespace-only or CRLF mismatches, and ambiguous edits give their match
count and line numbers. Two negotiated edit features add tolerant matching
(line-trimmed, indentation-flexible, whitespace-normalized) and replaceAll,
with per-edit match reporting only for requests that opt in.
@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-29T18:29:22.774421Z 5f927f8 Manual request
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 25adab506f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/code/src/edits.ts Outdated
Comment thread packages/code/src/edits.ts Outdated
Comment thread packages/code/src/edits.ts Outdated
Comment thread packages/code/src/protocol.ts
Comment thread packages/code/src/workspace.ts
Comment thread packages/code/src/edits.ts Outdated

@lia-by-librechat lia-by-librechat Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed and fixed all six open Codex findings at 8d1a44ecfb24fbb99ae7014a91d9b1d535389902:

  1. Whole whitespace-delimited token matching prevents partial identifier edits; the new streaming KMP matcher handles large inputs without compiling request-sized regexes.
  2. Ambiguous exact matches keep a count and at most five offsets, rather than allocating one object per hit.
  3. Preview-only workers can negotiate tolerant_match and replace_all without advertising the edit-only hash feature.
  4. The worker rejects unnegotiated matching and replaceAll before executing or arming an edit, even when those fields specify exact or false.
  5. KMP searches line-window candidates in linear time; repetitive indentation verification is bounded and fails closed with a useful conflict.

I also fixed an additional resource-limit issue: replaceAll now rejects an oversized intermediate before allocating a giant replacement. Previews and edits remain non-mutating on rejection. Existing legacy result shapes are unchanged.

Verification at this exact head: code-package tsc --noEmit passed; 99 focused engine/protocol/worker tests and 10 workspace integration tests passed; three restricted-heap regressions passed; 91 focused service tests and touched-file ESLint passed. Service tsc --noEmit still reports seven errors in pre-existing unrelated files. CI has nine passing checks; Lambda MicroVM Runner Image (arm64) is still running. Full suites, the code-package lint/import-sort (no configured executable or script), and a fresh Codex review were not run here. The GitHub App cannot trigger Codex; a maintainer can request it if desired.

@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review the latest head

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 8d1a44ecfb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/code/src/edits.ts Outdated

@lia-by-librechat lia-by-librechat Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Confirmed and fixed the Codex CRLF/LF boundary finding at exact head 4b677dfcf5266e86ced295cbd1576e18ba9c89fb. The regression failed before the fix and now passes. The whitespace-normalized tier normalizes caller newline forms before peeling, retains each matched line's ending in mixed files, and keeps exact-mode replacements unchanged.

The wider edit-subsystem review also found and fixed silent boundary errors: a token-only match no longer accepts oldText boundary whitespace or line breaks absent from the source, and rejects caller attempts to remove that out-of-range whitespace. Multiple claimed line breaks must actually exist. Previews and edits fail with EDIT_CONFLICT without modifying the file. BOM, batch operations, replaceAll, mixed line endings, wire results, and many-match performance have regressions.

Local checks: code-package TypeScript passed; 119 focused matcher/protocol/worker tests and 22 workspace tests passed; 83 service tests passed before the final boundary-only edit; service typecheck retains its seven pre-existing errors. Exact-head CI may still be running. Full suites, code-package lint/import-sort (not configured), and a new Codex review were not run; a maintainer must trigger Codex.

@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review the latest head

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 4b677dfcf5

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/code/src/edits.ts Outdated
Comment thread packages/code/src/edits.ts Outdated
Comment thread packages/code/src/edits.ts
Comment thread packages/code/src/edits.ts Outdated

@lia-by-librechat lia-by-librechat Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reviewed and addressed the four fresh Codex findings at 5ac2f45227413ae505890bbe8dd7e5082f65fa0a. They applied to the current head, not stale code.

  • Exact matches now use UTF-16 KMP to count overlapping locations in linear time; replaceAll still consumes nonoverlapping ranges. A long repetitive input previously exceeded the bounded regression runtime and now completes quickly.
  • Line windows and nearest-line hints share a compact newline index per source revision, invalidated after every successful edit. This replaces repeated eager allocation of hundreds of thousands of Line objects; a dense valid file with 80 failing edits now completes under a 96 MiB Node heap while keeping batch diagnostics.
  • Whitespace-normalized matching peels the whole equivalent leading or trailing newline-and-indentation wrapper rather than only the newline, so source indentation cannot be doubled. Previews and committed edits are covered.
  • Worker settlement suppresses edit error text when neither read_file nor preview_edit is advertised for that specific workspace. It retains the typed error code; read-authorized workspaces still get detailed diagnostics. An edit-only error is not a source-read grant. preview_edit already returns file contents, so it is treated as read-capable.

These regressions failed before the changes. Code-package tsc --noEmit, 129 focused matcher/protocol/worker tests, 23 preview/edit workspace tests, and two 96 MiB stress tests passed. Focused service routing tests: 91 passed. Service tsc --noEmit still has its seven baseline errors. Exact-head CI is pending. Not run: full suites, code-package lint/import-sort (not configured) and a fresh Codex review (maintainer trigger required). The wider review checked operation-scoped authorization, preview-to-edit fencing, atomic mutation failures, Unicode offsets, newline boundaries and memory/time bounds.

@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review the latest head

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5ac2f45227

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/code/src/edits.ts Outdated

@lia-by-librechat lia-by-librechat Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Rechecked PR #271 at a79d5db81576dec0c2ebe5c6b5d1544da0120d77. The new Codex P2 was fresh: a valid 1 MiB x -> x replaceAll exhausted a 64 MiB Node heap because it retained about one million range objects. That test failed before this commit and passes now. Match counting retains only the first range; replaceAll streams output in chunks (flushing at a 16 KiB threshold), skips identical pieces, and preserves the early 1 MiB output bound.

I checked all three matching tiers, overlapping versus non-overlapping matches, Unicode, ordered edits, error codes, BOM/preview/atomic commit, result matches.occurrences and negotiated feature boundaries. Four thousand deterministic exact-replacement cases matched a reference implementation. A 100-edit, 1 MiB no-op batch completed under a 64 MiB heap without changing the source.

Local checks passed: code-package npx tsc --noEmit, 55 matcher tests, 24 focused preview/edit integration tests, 21 edit/protocol tests, 27 workspace-worker tests and 83 focused service routing tests. The service typecheck retains seven previously observed errors. Exact-head CI is running. Full suites, code-package lint/import-sort (not configured) and a fresh Codex review (requires a maintainer trigger) were not run locally.

@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review, final review. no further reviews will be requested, last chance

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a79d5db815

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/code/src/edits.ts Outdated

@lia-by-librechat lia-by-librechat Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Reproduced and fixed the fresh Codex P2 at 5f927f80ca662bdf214e7068c42ccf988aa11861. When oldText and newText used different boundary indentation, the whitespace-normalized tier rejected an intentionally added leading blank line. A regression failed before the fix. The matcher now peels only the newline-and-indentation wrapper corresponding to oldText, keeping additional requested blank lines. The same rule applies to the trailing wrapper. It still rejects attempts to remove source whitespace outside the matched tokens.

Reviewed the full affected path: exact versus tolerant strategy order, source boundary presence, CRLF/LF conversion, per-match replaceAll, batch and BOM preview-to-edit fencing, result validation, edit authorization, and bounded-memory matching. Focused checks passed: code-package tsc --noEmit, 61 matcher tests, 25 workspace preview/edit tests, 77 protocol/worker tests, five cases under a 64 MiB heap, and 91 service routing tests. The service typecheck retains its seven pre-existing diagnostics. PR #272 is already merged into main; the branch is mergeable against that main and the new head's CI is running.

Not run locally: full suites, code-package lint/import-sort (no configured script), and a new Codex review (requires a maintainer trigger). This note does not claim CI completion or approve a merge.

@danny-avila

Copy link
Copy Markdown
Collaborator Author

@codex review the latest head, final review. no further reviews will be requested, last chance

@chatgpt-codex-connector chatgpt-codex-connector Bot 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5f927f80ca

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread packages/code/src/edits.ts Outdated
Comment thread packages/code/src/edits.ts Outdated
Comment thread packages/code/src/edits.ts Outdated
@lia-by-librechat

Copy link
Copy Markdown
Contributor

Addressed the three fresh Codex findings and one independently reproduced review finding at exact head 8a3c1bb5f7b0d0c46121d6d7fc825a5594218e8a.

  • Whitespace-only diagnostic probes preserve oldText's boundary wrappers.
  • Boundary shortcuts require the complete whitespace run, preventing duplicated source/replacement indentation; the leading/trailing LF/CRLF matrix passes.
  • One full linear indentation verification is permitted even above 100,000 lines, without removing the repetitive-candidate cap or materializing a large candidate window. A 330,000-line, 990,007-byte candidate passes under a 64 MiB heap.
  • Batch diagnostics now reserve every failing edit position before shortening long reasons. The independent review reproduced sparse position omissions on the previous head; parent matcher and preview/edit regressions independently confirmed the defect. All 100 failure positions fit within 3,000 characters. Short messages remain complete; truncated Unicode stays well formed.

Local checks passed: code-package tsc --noEmit, 147 engine/protocol/worker tests, 29 preview/edit integrations, seven bounded-memory cases, 297 diagnostic-count/Unicode comparisons, and 91 focused service routing tests. Service typecheck still reports the seven previously observed errors. Full suites and code-package lint/import-sort (not configured) were not run locally. Exact-head CI and a fresh independent review are running.

The earlier independent review was incomplete, not clean: its separate Finding Verifier could not initialize. Its reported defect has been reproduced and fixed here. The new review is tied to this exact head; no merge-readiness sign-off is being claimed yet. No additional Codex cycle is being requested.

@lia-by-librechat

Copy link
Copy Markdown
Contributor

Independent review completed for exact head 8a3c1bb5f7b0d0c46121d6d7fc825a5594218e8a, base main at 3189cfd8ee7f81cf90671f9b91b1d52ae9d3693d, merge-base 5b71e8c14d8843e725467174b2684debdf31ab58.

Result: Complete, no findings. The reviewer independently read the complete 14-file diff and traced matching, negotiation, result validation, operation-scoped authorization and atomic commit. All 70 edit-engine tests and 19 focused protocol/worker/workspace tests passed. It confirmed all three Codex fixes and the additional diagnostic-position fix. The 60-edit preview and mutation regression retained all 30 odd-numbered failing positions within 3,000 characters while leaving the source unchanged.

All four finding dispositions: fixed. No unresolved review threads remain at this head. The reviewer did not execute service bridge integration tests because Bun was unavailable in its review context; the parent run executed 91 focused service routing tests successfully. Parent code-package typecheck, 147 matcher/protocol/worker tests, 29 preview/edit integrations and seven bounded-memory cases passed. Service typecheck retains seven previously observed errors. Full suites and code-package lint/import-sort (not configured) were not run locally.

Current exact-head CI: 10 successful checks, no failures; Lambda MicroVM Runner Image (arm64) still running. The PR is mergeable but requires approval. No merge or deployment was performed.

@danny-avila
danny-avila merged commit 1e683ce into main Sep 30, 2026
11 checks passed
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.

2 participants