{"record":{"id":"595f61bd9bd93ee7","repo":"coleam00/Archon","slug":"result-message","errorCode":null,"errorMessage":"${result.message}","messagePattern":"\\$\\{result\\.message\\}","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/workflows/src/dry-run.ts","lineNumber":515,"sourceCode":"\nasync function loadDryRunCommand(ctx: DryRunContext, command: string): Promise<string> {\n  const result = await loadCommandPrompt(\n    {\n      // Unreachable: `loadCommandPrompt` consults `loadConfig` only when its source roots\n      // carry no `load_default_commands`, and `ctx.sourceRoots` always does — the field is\n      // a required boolean on every roots value. Throwing rather than returning a stub\n      // config keeps a future signature change from silently resolving commands against a\n      // fabricated policy.\n      loadConfig: () => {\n        throw new Error('dry-run resolves commands from its source roots, never from config');\n      },\n    },\n    ctx.cwd,\n    command,\n    undefined,\n    ctx.sourceRoots\n  );\n  if (!result.success) throw new Error(result.message);\n  return result.content;\n}\n\nfunction resolveText(\n  text: string,\n  ctx: DryRunContext,\n  outputs: Map<string, NodeOutput>,\n  shellSafe = false,\n  loopPrevOutput = '',\n  escapeNodeOutputs = shellSafe,\n  // The `$INPUTS` bag for this text — run-level inputs unless the node carries\n  // node-local `with:` bindings, which merge OVER them (#2637; nearest wins,\n  // matching the executor's command-prompt path).\n  inputs: Record<string, JsonValue> | undefined = ctx.inputs\n): string {\n  const docsDir = join(ctx.cwd, 'docs');\n  const substituted = substituteWorkflowVariables(\n    text,","sourceCodeStart":497,"sourceCodeEnd":533,"githubUrl":"https://github.com/coleam00/Archon/blob/0773b9745896ef0612e709c80845a0f7db315b19/packages/workflows/src/dry-run.ts#L497-L533","documentation":"During a dry-run simulation, when a workflow node loads a command prompt, `loadDryRunCommand` calls `loadCommandPrompt` and propagates any failure by throwing a plain Error carrying the loader's message. The library throws this because a dry run must resolve the exact command text the real run would use; if the command cannot be loaded from the dry-run source roots, simulation cannot proceed honestly.","triggerScenarios":"Calling `loadDryRunCommand(ctx, command)` (via simulateLoop/sourceText) when `loadCommandPrompt` returns `{success: false}` — typically because the referenced command file is missing from `ctx.sourceRoots`, unreadable, or fails frontmatter/parse validation. Note the dry-run path never falls back to `loadConfig`; commands resolve only from the provided source roots.","commonSituations":"Running `archon dry-run` against a repo whose `commands/` directory was renamed or moved, a typo in a node's `command:` reference, a command file present only in a config dir that dry-run intentionally ignores, or a YAML syntax error inside the command file.","solutions":["Verify the command file exists at the path the node references, within the source roots passed to the dry run","Fix syntax/frontmatter errors in the command file (run the loader or a YAML parser on it)","Check that ctx.sourceRoots points at the correct repository root and includes the commands directory","Run the same command load in a real (non-dry) run to compare the resolved source roots"],"exampleFix":"// before: command referenced by a node does not exist\ncommand: deploy-steps   // no commands/deploy-steps.md in source roots\n// after\ncommand: deploy-steps   // commands/deploy-steps.md present in repo","handlingStrategy":"try-catch","validationCode":"import { existsSync, statSync } from 'node:fs';\nimport { join } from 'node:path';\n// Verify the command file resolves inside one of the source roots before dry-running.\nfunction commandFileExists(command: string, sourceRoots: string[]): boolean {\n  return sourceRoots.some((root) => {\n    for (const rel of [`commands/${command}.md`, `${command}.md`]) {\n      const p = join(root, rel);\n      if (existsSync(p) && statSync(p).isFile()) return true;\n    }\n    return false;\n  });\n}","typeGuard":null,"tryCatchPattern":"try {\n  const content = await loadDryRunCommand(ctx, command);\n} catch (err) {\n  // loadDryRunCommand throws the loader's message verbatim; rethrow with the command name for context.\n  throw new Error(`dry-run: cannot load command '${command}': ${(err as Error).message}`, { cause: err });\n}","preventionTips":["Keep command files inside the directories listed in sourceRoots","Lint workflow command: references against the files present in commands/","Run the dry run from the repository root so relative command paths resolve","Validate command-file YAML/frontmatter in CI"],"tags":["dry-run","command-loading","filesystem","workflow"],"backgroundTag":"command-file-not-found","analyzedSha":"0773b9745896ef0612e709c80845a0f7db315b19","analyzedAt":"2026-09-01T02:28:07.064Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}