{"record":{"id":"4ee7a775110a1fc0","repo":"JuliusBrussee/caveman","slug":"hermes-routing-block-changed-after-enable-refusin","errorCode":null,"errorMessage":"Hermes routing block changed after enable; refusing destructive disable","messagePattern":"Hermes routing block changed after enable; refusing destructive disable","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/cli/src/index.ts","lineNumber":8410,"sourceCode":"    }\n    text = text.replace(routeBlock, typeof previousRouteLine === \"string\" ? previousRouteLine : \"\");\n    text = text.replace(readBlock, \"\");\n    return Buffer.from(text.replace(/\\n{3,}/g, \"\\n\\n\"));\n  }\n  if (operation.kind === \"aider-core\") {\n    throw new Error(`${operation.file} changed after enable; refusing destructive disable`);\n  }\n  if (operation.kind === \"hermes-plugin-manifest\" || operation.kind === \"hermes-plugin-init\") {\n    throw new Error(`${operation.file} changed after enable; refusing destructive disable`);\n  }\n  if (operation.kind === \"hermes-config\") {\n    let text = current.toString(\"utf8\");\n    const routeBlock = operation.owned?.route_block;\n    const previousRouteLines = operation.owned?.previous_route_lines;\n    if (typeof routeBlock !== \"string\" || !Array.isArray(previousRouteLines)) {\n      throw new Error(\"Hermes integration journal lacks owned routing block\");\n    }\n    if (!text.includes(routeBlock)) throw new Error(\"Hermes routing block changed after enable; refusing destructive disable\");\n    text = text.replace(routeBlock, previousRouteLines.filter((line): line is string => typeof line === \"string\").join(\"\\n\"));\n    for (const key of [\"plugin_block\", \"mcp_block\"] as const) {\n      const block = operation.owned?.[key];\n      if (block === null || block === undefined) continue;\n      if (typeof block !== \"string\" || !text.includes(block)) throw new Error(`Hermes ${key.replace(\"_block\", \"\")} block changed after enable; refusing destructive disable`);\n      text = text.replace(block, \"\");\n    }\n    return Buffer.from(text);\n  }\n  const rootBlock = operation.owned?.root_block;\n  const tablesBlock = operation.owned?.tables_block;\n  if (typeof rootBlock !== \"string\" || typeof tablesBlock !== \"string\") throw new Error(\"Codex integration journal lacks owned blocks\");\n  let text = current.toString(\"utf8\");\n  for (const [block, begin] of [[rootBlock, CODEX_NATIVE_ROOT_BEGIN], [tablesBlock, CODEX_NATIVE_TABLES_BEGIN]] as const) {\n    if (text.includes(begin) && !text.includes(block)) throw new Error(\"Codex Caveman config block changed after enable; refusing destructive disable\");\n    text = text.replace(`${block}\\n\\n`, \"\").replace(`\\n\\n${block}\\n`, \"\\n\").replace(block, \"\");\n  }\n  return Buffer.from(text);","sourceCodeStart":8392,"sourceCodeEnd":8428,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/packages/cli/src/index.ts#L8392-L8428","documentation":"During Hermes config disable, the journaled routing block must still exist verbatim in the config file. If route_block is not found in the text, the file was modified after enable and the tool refuses to disable destructively, protecting user edits from being clobbered by blind text replacement.","triggerScenarios":"Running disable with kind === \"hermes-config\" when !text.includes(routeBlock) — the routing block was edited, reformatted, commented out, or partially deleted after enable.","commonSituations":"User tweaked routing rules inside the Caveman block; a config formatter changed indentation/line endings; user already manually removed part of the block.","solutions":["Restore the routing block to its exact journaled content (git checkout/backup), then re-run disable.","Manually delete the Caveman routing block from the Hermes config.","Re-run enable to rewrite the block and refresh the journal, then disable."],"exampleFix":"// before: whitespace changed inside block\n# caveman-route-start\n  route: caveman   # reformatted\n# caveman-route-end\n// after: exact original restored\n# caveman-route-start\nroute: caveman\n# caveman-route-end","handlingStrategy":"validation","validationCode":"const text = fs.readFileSync(hermesConfig, 'utf8');\nconst rb = journal.owned?.route_block;\nif (typeof rb !== 'string' || !text.includes(rb)) {\n  throw new Error('Hermes routing block drift detected; restore it before disable');\n}","typeGuard":"const routeBlockIntact = (text, owned) =>\n  typeof owned?.route_block === 'string' && text.includes(owned.route_block);","tryCatchPattern":"try {\n  await caveman.disable(['hermes']);\n} catch (err) {\n  if (err.message.includes('Hermes routing block changed')) {\n    console.error('Restore the exact routing block (git checkout) or delete it manually.');\n  } else throw err;\n}","preventionTips":["Commit the Hermes config before enable.","Don't tweak routing rules inside the marked Caveman block; configure routing elsewhere.","Watch for formatters/linters rewriting the config file.","Re-run enable after any intentional change to refresh the journal."],"tags":["config","hermes","disable-safety"],"backgroundTag":"conflicting-config-options","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}