Problem
The hoisted AFT edit tool rejects find/replace requests when unused line-range fields are also present in the same edits[] item.
The request fails with:
edit: edits[0] mixes find/replace and line-range fields
This can happen when the model or host serializer emits optional fields from both edit modes, even though only find/replace was intended.
Reproduction
Send the following to the hoisted edit tool:
{
"path": "example.ts",
"edits": [
{
"oldString": "before",
"newString": "after",
"startLine": 1,
"endLine": 1,
"content": ""
}
]
}
The intended operation is a find/replace. The line-range fields are unused serializer/model fields, but their presence makes AFT treat the item as both a find/replace edit and a line-range edit.
Expected behavior
AFT should expose mutually exclusive edit-item shapes so that a find/replace item can contain only find/replace fields:
{
"path": "example.ts",
"edits": [
{
"oldString": "before",
"newString": "after"
}
]
}
A line-range item should remain a separate shape:
{
"path": "example.ts",
"edits": [
{
"startLine": 10,
"endLine": 12,
"content": "replacement"
}
]
}
Genuinely mixed edits should continue to be rejected.
Likely cause
The public BatchEditParams schema defines all of these as optional fields on one object:
- Find/replace:
oldString, newString, replaceAll, occurrence
- Line range:
startLine, endLine, content
The schema does not express that these are two mutually exclusive shapes. AFT later checks which keys are present and rejects the mixed object, so the request is allowed by the model-facing schema and then rejected during normalization.
The current runtime check is reasonable for a genuinely mixed edit, but it cannot distinguish an intentionally empty line-1 deletion from serializer-generated line-range defaults once both modes have been sent. The schema should prevent the ambiguity at the source.
Proposed fixes
- Define
edits[] as a union of two closed object shapes: find/replace and line range.
- Ensure unused optional fields are omitted rather than serialized as empty strings,
null, or default values.
- Keep runtime rejection for meaningful mixed edits as a safety check.
- Add regression tests for find/replace items carrying empty/default line-range fields, alongside the existing tests for the opposite direction and null-valued fields.
This is a follow-up to #217 and #272, which address related sentinel cases but not this reverse combination of non-empty find/replace fields with line-range fields.
Environment
@cortexkit/aft-pi: 0.54.0
- Pi with AFT's hoisted
edit tool
Problem
The hoisted AFT
edittool rejects find/replace requests when unused line-range fields are also present in the sameedits[]item.The request fails with:
This can happen when the model or host serializer emits optional fields from both edit modes, even though only find/replace was intended.
Reproduction
Send the following to the hoisted
edittool:{ "path": "example.ts", "edits": [ { "oldString": "before", "newString": "after", "startLine": 1, "endLine": 1, "content": "" } ] }The intended operation is a find/replace. The line-range fields are unused serializer/model fields, but their presence makes AFT treat the item as both a find/replace edit and a line-range edit.
Expected behavior
AFT should expose mutually exclusive edit-item shapes so that a find/replace item can contain only find/replace fields:
{ "path": "example.ts", "edits": [ { "oldString": "before", "newString": "after" } ] }A line-range item should remain a separate shape:
{ "path": "example.ts", "edits": [ { "startLine": 10, "endLine": 12, "content": "replacement" } ] }Genuinely mixed edits should continue to be rejected.
Likely cause
The public
BatchEditParamsschema defines all of these as optional fields on one object:oldString,newString,replaceAll,occurrencestartLine,endLine,contentThe schema does not express that these are two mutually exclusive shapes. AFT later checks which keys are present and rejects the mixed object, so the request is allowed by the model-facing schema and then rejected during normalization.
The current runtime check is reasonable for a genuinely mixed edit, but it cannot distinguish an intentionally empty line-1 deletion from serializer-generated line-range defaults once both modes have been sent. The schema should prevent the ambiguity at the source.
Proposed fixes
edits[]as a union of two closed object shapes: find/replace and line range.null, or default values.This is a follow-up to #217 and #272, which address related sentinel cases but not this reverse combination of non-empty find/replace fields with line-range fields.
Environment
@cortexkit/aft-pi:0.54.0edittool