{"record":{"id":"452b5f72bba83eea","repo":"can1357/oh-my-pi","slug":"operation-operationnumber-has-pattern-selecti","errorCode":null,"errorMessage":"Operation ${operationNumber} has ${pattern.selectionRanges.length} selections, but REWRITE proves neither positional substitution nor whole-span replacement.\nCopy-ready per-selection interpretation:\n${header}\n${operation.patternText}\n${REWRITE_HEADER}\n${repeated.join(\"\\n\")}\nCopy-ready whole-span interpretation:\n${header}\n${operation.patternText}\n${REWRITE_HEADER}\n${rewriteSelectionSpans(content, candidate, repeated)}","messagePattern":"Operation (.+?) has (.+?) selections, but REWRITE proves neither positional substitution nor whole-span replacement\\.\nCopy-ready per-selection interpretation:\n(.+?)\n(.+?)\n(.+?)\n(.+?)\nCopy-ready whole-span interpretation:\n(.+?)\n(.+?)\n(.+?)\n(.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/edit/sloppy.ts","lineNumber":3711,"sourceCode":"\t\t\t\t\tlastMatchOffset = candidate.matchStart;\n\t\t\t\t}\n\t\t\t\tif (positionalChanges === 0) {\n\t\t\t\t\tconst hint = positionalWouldChangeHint(content, candidates[0], pattern, segments);\n\t\t\t\t\tthrowNoOp(\n\t\t\t\t\t\toperationNumber,\n\t\t\t\t\t\t{ content, offset: candidates[0].matchStart },\n\t\t\t\t\t\toperation.all ? candidates.length : undefined,\n\t\t\t\t\t\thint,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!candidates.every(candidate => rewriteProvesWholeSpan(content, candidate, baseResolvedRewrite))) {\n\t\t\t\tconst candidate = candidates[0];\n\t\t\t\tconst oneLineRewrite = baseResolvedRewrite.replace(/\\s*\\n\\s*/gu, \" \");\n\t\t\t\tconst repeated = new Array<string>(pattern.selectionRanges.length).fill(oneLineRewrite);\n\t\t\t\tconst header = operation.all ? `${OPENER}*` : OPENER;\n\t\t\t\tthrow new Error(\n\t\t\t\t\t[\n\t\t\t\t\t\t`Operation ${operationNumber} has ${pattern.selectionRanges.length} selections, but REWRITE proves neither positional substitution nor whole-span replacement.`,\n\t\t\t\t\t\t\"Copy-ready per-selection interpretation:\",\n\t\t\t\t\t\t`${header}\\n${operation.patternText}\\n${REWRITE_HEADER}\\n${repeated.join(\"\\n\")}`,\n\t\t\t\t\t\t\"Copy-ready whole-span interpretation:\",\n\t\t\t\t\t\t`${header}\\n${operation.patternText}\\n${REWRITE_HEADER}\\n${rewriteSelectionSpans(content, candidate, repeated)}`,\n\t\t\t\t\t].join(\"\\n\"),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\t\tlet changes = 0;\n\t\tfor (const located of orderedCandidates) {\n\t\t\tlet candidate = located;\n\t\t\tlet resolvedCandidateRewrite = baseResolvedRewrite;\n\t\t\tif (loneReference && !operation.all) {\n\t\t\t\tconst referenced = removedByOperation[Number(loneReference[1]) - 1];\n\t\t\t\tconst anchorLineStart = content.lastIndexOf(\"\\n\", Math.max(0, candidate.matchStart - 1)) + 1;\n\t\t\t\tconst anchorNewline = content.indexOf(\"\\n\", candidate.matchEnd);","sourceCodeStart":3693,"sourceCodeEnd":3729,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/edit/sloppy.ts#L3693-L3729","documentation":"When a pattern has multiple selections, REWRITE must provably mean one of two things: per-selection substitution (the rewrite text is the replacement for each selection) or whole-span replacement (the rewrite replaces the entire matched span, verifiable against candidate content). If rewriteProvesWholeSpan fails for every candidate and positional substitution does not apply, the library throws a diagnostic containing two fully copy-ready alternative payloads — one per-selection, one whole-span — so the author can pick the intended interpretation.","triggerScenarios":"An operation with multiple selectionRanges where the base resolved REWRITE neither matches repeated per-selection substitution nor passes rewriteProvesWholeSpan against any candidate.","commonSituations":"Ambiguous multi-selection rewrites where the author meant one of the two semantics; model payloads mixing per-selection text with whole-span text; edits where whitespace differences break the whole-span proof.","solutions":["Pick the intended interpretation from the two copy-ready payloads in the error and resubmit that exact payload.","For per-selection semantics: repeat the rewrite text once per selection in REWRITE.","For whole-span semantics: rewrite the entire matched span as final text (use the payload whose REWRITE was generated by rewriteSelectionSpans)."],"exampleFix":"// before (ambiguous)\n»\n<sel>a</sel> <sel>b</sel>\n====\nX\n\n// after (per-selection chosen)\n»\n<sel>a</sel> <sel>b</sel>\n====\nX\nX","handlingStrategy":"retry","validationCode":"function disambiguateMultiSelection(rewrite: string, selectionCount: number): string {\n  // choose per-selection semantics explicitly: repeat rewrite once per selection\n  return Array.from({ length: selectionCount }, () => rewrite).join(\"\\n\");\n}","typeGuard":null,"tryCatchPattern":"try {\n  applySloppyEdit(payload);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"proves neither positional substitution nor whole-span replacement\")) {\n    const perSelection = err.message.split(\"Copy-ready per-selection interpretation:\\n\")[1]\n      ?.split(\"Copy-ready whole-span interpretation:\")[0];\n    if (perSelection) applySloppyEdit(perSelection); else throw err;\n  } else throw err;\n}","preventionTips":["For multi-selection patterns, commit to per-selection substitution (repeat the text per selection) or whole-span replacement explicitly","Copy one of the two ready-made payloads from the error rather than hand-editing","Keep REWRITE whitespace consistent with the pattern segments so the whole-span proof can succeed"],"tags":["edit-dsl","ambiguous-rewrite","multi-selection"],"backgroundTag":"ambiguous-edit-rewrite","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}