{"record":{"id":"44aabc328f7ffac9","repo":"can1357/oh-my-pi","slug":"rewrite-header-reference-1-must-reference-an","errorCode":null,"errorMessage":"${REWRITE_HEADER}${reference[1]} must reference an earlier operation, not self/forward.","messagePattern":"(.+?)(.+?) must reference an earlier operation, not self/forward\\.","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/edit/sloppy.ts","lineNumber":1414,"sourceCode":"\t\t) {\n\t\t\t// A lone ⟫ with no open selection is a stray block terminator; REWRITE\n\t\t\t// is final text and never carries selection markers.\n\t\t} else {\n\t\t\trewriteLines.push(line);\n\t\t}\n\t}\n\n\tif (state === \"rewrite\") finish(lines.length);\n\telse if (state === \"pattern\") finishPattern(lines.length);\n\tif (operations.length === 0) throw new Error(`Empty patch. Start with ${OPENER}.`);\n\tfor (let index = 0; index < operations.length; index++) {\n\t\tconst operationRewrite = operations[index].rewrite;\n\t\tconst rewrites = operationRewrite.kind === \"explicit\" ? [operationRewrite.text] : operationRewrite.replacements;\n\t\tfor (const rewrite of rewrites) {\n\t\t\tfor (const line of rewrite.split(\"\\n\")) {\n\t\t\t\tconst reference = line.trim().match(/^»([1-9]\\d*)$/u);\n\t\t\t\tif (reference && Number(reference[1]) >= index + 1) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`${REWRITE_HEADER}${reference[1]} must reference an earlier operation, not self/forward.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tfor (const [index, message] of pendingSeparatorErrors) {\n\t\tconst patternNormalized = normalizeText(operations[index].patternText).text;\n\t\tconst justified = operations.some((other, otherIndex) => {\n\t\t\tif (otherIndex === index) return false;\n\t\t\tconst rewrites = other.rewrite.kind === \"explicit\" ? [other.rewrite.text] : other.rewrite.replacements;\n\t\t\treturn rewrites.some(\n\t\t\t\trewrite =>\n\t\t\t\t\tnormalizeText(rewrite).text.includes(patternNormalized) ||\n\t\t\t\t\trewrite.split(\"\\n\").some(line => line.trim() === `${REWRITE_HEADER}${index + 1}`),\n\t\t\t);\n\t\t});\n\t\tif (!justified) throw new Error(message);","sourceCodeStart":1396,"sourceCodeEnd":1432,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/edit/sloppy.ts#L1396-L1432","documentation":"Inside a rewrite, a »N line reuses the replacement text of operation N — but only backwards: N must be strictly less than the current operation number (operations are 1-based, so line »2 inside operation 2 is self-reference and rejected). Self- and forward-references would be circular or undefined at apply time, so the parser rejects them up front during post-parse validation.","triggerScenarios":"A rewrite block (explicit text or a »-reference chain) containing a »N line where N >= the current operation's index+1 — e.g. operation 1 containing »1, or operation 3 containing »4.","commonSituations":"Models numbering references from 0 or miscounting operation order; copying a register line into the operation it defines; reordering operations after writing references without renumbering.","solutions":["Change the reference to point at a strictly earlier operation (renumber if you reordered operations).","Inline the desired text directly instead of referencing if no earlier operation holds it.","Recount operations from 1 in payload order and fix all »N lines to satisfy N < current."],"exampleFix":"// before (operation 3 referencing itself)\n«\npattern\n»\n»3\n\n// after\n«\npattern\n»\n»1","handlingStrategy":"validation","validationCode":"let op = 0;\nfor (const line of body.split(\"\\n\")) {\n  const t = line.trim();\n  if (t === \"«\" || t === \"«*\") op++;\n  const ref = t.match(/^»([1-9]\\d*)$/);\n  if (ref && Number(ref[1]) >= op && op > 0) throw new Error(`»${ref[1]} is not an earlier operation (current: ${op})`);\n}","typeGuard":"const isBackwardReference = (n: number, currentOp: number): boolean => n > 0 && n < currentOp;","tryCatchPattern":"catch (err) {\n  if (err instanceof Error && err.message.includes(\"must reference an earlier operation\")) {\n    // renumber »N references after any operation reordering\n  }\n}","preventionTips":["After reordering or splicing operations, renumber all »N references.","Reference numbering is 1-based payload order; reference only ops defined earlier.","Prefer inlining text over »N chains when the chain is short."],"tags":["parser","edit-format","reference-validation"],"backgroundTag":"invalid-reference","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}