{"record":{"id":"257c5f27faa82655","repo":"can1357/oh-my-pi","slug":"operation-operationnumber-pattern-is-too-generi","errorCode":null,"errorMessage":"Operation ${operationNumber} pattern is too generic; include a distinctive name or statement.","messagePattern":"Operation (.+?) pattern is too generic; include a distinctive name or statement\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/edit/sloppy.ts","lineNumber":1573,"sourceCode":"\n\tlet strippedLeading = 0;\n\twhile (tokens[0]?.kind === \"gap\") {\n\t\ttokens.shift();\n\t\tstrippedLeading++;\n\t}\n\twhile (tokens.at(-1)?.kind === \"gap\") tokens.pop();\n\tfor (let index = 0; index < selectionBoundaries.length; index++) {\n\t\tselectionBoundaries[index] = Math.max(0, Math.min(tokens.length, selectionBoundaries[index] - strippedLeading));\n\t}\n\tconst literals = tokens.filter((token): token is LiteralToken => token.kind === \"literal\");\n\tif (literals.length === 0) throw new Error(`Operation ${operationNumber} needs visible current text.`);\n\t// Only punctuation-only anchors (`}`, `};`, `);`) are genuinely too generic:\n\t// they match everywhere and their candidate lists are noise. Any identifier\n\t// text — however short (`id`, `avlue`) — is a legitimate anchor; uniqueness\n\t// (or an explicit `«*`) decides whether it applies, not its length.\n\tconst hasIdentifierText = literals.some(token => /[\\p{L}\\p{N}_$]/u.test(token.normalized));\n\tif (!hasIdentifierText) {\n\t\tthrow new Error(`Operation ${operationNumber} pattern is too generic; include a distinctive name or statement.`);\n\t}\n\n\tconst emptyDoubleSelection = selectionBoundaries.length === 2 && selectionBoundaries[0] === selectionBoundaries[1];\n\tconst insertion = selectionBoundaries.length === 1 || emptyDoubleSelection;\n\tconst explicitSingleSelection = selectionBoundaries.length === 2 && !emptyDoubleSelection;\n\tconst selectionStart = insertion || explicitSingleSelection ? selectionBoundaries[0] : 0;\n\tconst selectionEnd = insertion ? selectionStart : explicitSingleSelection ? selectionBoundaries[1] : tokens.length;\n\tconst selectionPairs =\n\t\tselectionBoundaries.length > 0 && selectionBoundaries.length % 2 === 0\n\t\t\t? Array.from({ length: selectionBoundaries.length / 2 }, (_, index) => {\n\t\t\t\t\tconst start = selectionBoundaries[index * 2];\n\t\t\t\t\tconst end = selectionBoundaries[index * 2 + 1];\n\t\t\t\t\treturn {\n\t\t\t\t\t\tstart,\n\t\t\t\t\t\tend,\n\t\t\t\t\t\tcaptureIndices: tokens\n\t\t\t\t\t\t\t.slice(start, end)\n\t\t\t\t\t\t\t.filter((token): token is GapToken => token.kind === \"gap\")","sourceCodeStart":1555,"sourceCodeEnd":1591,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/edit/sloppy.ts#L1555-L1591","documentation":"The pattern has literal tokens, but every literal is punctuation-only (no letters, digits, `_`, or `$`). Punctuation-only anchors like `}`, `};`, or `);` match nearly everywhere in a source file, so the library refuses the pattern as too generic and asks for a distinctive name or statement. Identifier text — however short — is accepted; only punctuation is rejected.","triggerScenarios":"Building an operation whose pattern is just `}`, `};`, `);`, or similar closing punctuation with a gap, e.g. `...\\n};`. Detected via `literals.some(token => /[\\p{L}\\p{N}_$]/u.test(token.normalized))` returning false.","commonSituations":"Targeting a block's closing brace to insert before/after it without naming the block; minified or template files where the author grabbed only delimiters; assuming the tool can disambiguate 'the last }' without context.","solutions":["Include a distinctive identifier, name, or statement in the pattern (e.g. the function name before the closing brace).","Add more surrounding lines so the anchor contains identifier text.","If genuinely intending 'everywhere', check whether the tool offers an explicit unambiguous mode (`«*`) rather than a generic punctuation anchor."],"exampleFix":"// before\nconst pattern = \"...\\n};\";\n// after\nconst pattern = \"export function render() {\\n...\\n};\";","handlingStrategy":"validation","validationCode":"const literals = pattern.replace(/\\.\\.\\./g, \"\").trim();\nif (!/[\\p{L}\\p{N}_$]/u.test(literals)) {\n  throw new Error(\"pattern is punctuation-only; include a distinctive name or statement\");\n}","typeGuard":"function hasIdentifierAnchor(pattern: string): boolean {\n  return /[\\p{L}\\p{N}_$]/u.test(pattern.replace(/«|»|\\.\\.\\./g, \"\"));\n}","tryCatchPattern":"try {\n  await applySloppyEdit({ pattern });\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"too generic\")) {\n    // widen pattern to include the named declaration the punctuation belongs to\n  }\n  throw err;\n}","preventionTips":["Never anchor on bare closing punctuation (`}`, `};`, `);`).","Include the function/class/variable name that owns the block you are editing.","Remember: identifier text of any length counts; punctuation alone never does."],"tags":["edit-pattern","validation","anchor-too-generic"],"backgroundTag":"edit-pattern-too-generic","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}