{"record":{"id":"8ed423d38a5d2927","repo":"can1357/oh-my-pi","slug":"operation-operationnumber-has-adjacent-gap","errorCode":null,"errorMessage":"Operation ${operationNumber} has adjacent ${GAP}; use one ellipsis.","messagePattern":"Operation (.+?) has adjacent (.+?); use one ellipsis\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/edit/sloppy.ts","lineNumber":1517,"sourceCode":"\tconst tokens: PatternToken[] = [];\n\tlet literal = \"\";\n\tlet captureCount = 0;\n\tconst selectionBoundaries: number[] = [];\n\tconst selectionAtLineStart: boolean[] = [];\n\tconst selectionRawOffsets: number[] = [];\n\tconst flushLiteral = () => {\n\t\tif (literal === \"\") return;\n\t\tconst normalized = normalizeText(literal).text;\n\t\tif (normalized !== \"\") tokens.push({ kind: \"literal\", text: literal, normalized });\n\t\tliteral = \"\";\n\t};\n\n\tfor (let index = 0; index < pattern.length; ) {\n\t\tconst gapMarker = patternGapAt(pattern, index);\n\t\tif (gapMarker) {\n\t\t\tflushLiteral();\n\t\t\tif (tokens.at(-1)?.kind === \"gap\") {\n\t\t\t\tthrow new Error(`Operation ${operationNumber} has adjacent ${GAP}; use one ellipsis.`);\n\t\t\t}\n\t\t\tconst lineStart = pattern.lastIndexOf(\"\\n\", Math.max(0, index - 1)) + 1;\n\t\t\tconst nextNewline = pattern.indexOf(\"\\n\", index + gapMarker.length);\n\t\t\tconst lineEnd = nextNewline === -1 ? pattern.length : nextNewline;\n\t\t\tconst before = pattern.slice(lineStart, index).replaceAll(SELECT_OPEN, \"\").replaceAll(SELECT_CLOSE, \"\").trim();\n\t\t\tconst after = pattern\n\t\t\t\t.slice(index + gapMarker.length, lineEnd)\n\t\t\t\t.replaceAll(SELECT_OPEN, \"\")\n\t\t\t\t.replaceAll(SELECT_CLOSE, \"\")\n\t\t\t\t.trim();\n\t\t\ttokens.push({\n\t\t\t\tkind: \"gap\",\n\t\t\t\tcaptureIndex: captureCount++,\n\t\t\t\tlineBounded: before !== \"\" && after !== \"\",\n\t\t\t});\n\t\t\tindex += gapMarker.length;\n\t\t\tcontinue;\n\t\t}","sourceCodeStart":1499,"sourceCodeEnd":1535,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/edit/sloppy.ts#L1499-L1535","documentation":"The pattern tokenizer throws when two gap markers (`...`) appear directly adjacent in the token stream with no literal text between them. Adjacent gaps are redundant — a single `...` already means 'skip any content' — so the parser fails fast instead of accepting a meaningless double ellipsis.","triggerScenarios":"Writing a pattern like `foo();\\n...\\n...\\nbar();` or `... ...` where two ellipses touch with only whitespace between them. Detected in tokenizePattern when a new gap marker is seen while the last token is already kind \"gap\".","commonSituations":"Doubling ellipses to 'skip more lines' (misconception — one gap skips any amount); copy-paste artifacts leaving two `...` lines next to each other; template-generated patterns concatenating gap fragments without deduplication.","solutions":["Replace consecutive `...` markers with a single `...`.","If you meant to show skipped content separately, put a real literal anchor line between the two gaps.","If the extra gap was accidental from copy-paste, delete the duplicate line."],"exampleFix":"// before\nconst pattern = \"function foo() {\\n...\\n...\\n}\";\n// after\nconst pattern = \"function foo() {\\n...\\n}\";","handlingStrategy":"validation","validationCode":"if (/\\.\\.\\.[\\s]*\\.\\.\\./.test(pattern)) {\n  throw new Error(\"pattern contains adjacent ... markers; collapse to one\");\n}","typeGuard":"function hasAdjacentGaps(pattern: string): boolean {\n  return /\\.\\.\\.\\s*\\.\\.\\./.test(pattern);\n}","tryCatchPattern":"try {\n  await applySloppyEdit({ pattern });\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"adjacent\") && err.message.includes(\"ellipsis\")) {\n    pattern = pattern.replace(/(\\.\\.\\.\\s*)+\\.\\.\\./g, \"...\");\n    return applySloppyEdit({ pattern });\n  }\n  throw err;\n}","preventionTips":["One `...` skips any amount of content — never repeat it.","Deduplicate gap markers when concatenating pattern fragments in generators.","Review copy-pasted patterns for stray duplicate ellipsis lines."],"tags":["edit-pattern","validation","gap-marker"],"backgroundTag":"duplicate-gap-marker","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}