resolveEditMode function
Resolves an edit tool call's mode from its raw arguments (UT-1..3, E1).
Deterministic policy (card OQ1, patch-first):
- A usable patch wins whenever present — the hashline header is self-validating and a stale tag rejects before any write, so a complete patch is the safer of the two modes.
- A malformed patch with a complete exact-match triple falls back to exact-match (AC2) instead of rejecting.
- Only "neither mode complete" rejects, with the remedy example (AC3).
Implementation
EditModePlan resolveEditMode({
required String? path,
required String? oldText,
required String? newText,
required String? patch,
}) {
final exactComplete = exactMatchComplete(
path: path,
oldText: oldText,
newText: newText,
);
// ONE parse, honoring the executor's fallbackPath contract, carried
// through the plan (issue #862 review).
final parse = parsePatch(patch, fallbackPath: path);
if (parse.usable) {
final mixed = oldText != null || newText != null;
return EditRunPatch(
parsed: parse.patch!,
notice: mixed
? bothModesNotice(winner: 'the hashline patch')
: null,
);
}
if (exactComplete) {
final patchSent = patch != null && patch.trim().isNotEmpty;
return EditRunExactMatch(
path: path!,
oldText: oldText!,
newText: newText!,
notice: patchSent
? bothModesNotice(winner: 'the exact-match replace')
: null,
);
}
if (patch != null && patch.trim().isNotEmpty) {
// A patch was attempted but is malformed, and exact-match cannot
// rescue the call: reject with the parser's focused diagnostic plus
// the remedy (AC3's sibling shape).
return EditReject(
'The patch input is not a valid hashline patch '
'(${parse.error}), and the exact-match arguments are '
'incomplete. $editRemedyExample',
);
}
return EditReject(
'Missing arguments: provide either patch (hashline mode) or path + '
'oldText + newText (exact-match mode). $editRemedyExample',
);
}