{"record":{"id":"d565c40775b4df2e","repo":"Yeachan-Heo/oh-my-codex","slug":"mission-dir-must-be-inside-a-git-repository","errorCode":null,"errorMessage":"mission-dir must be inside a git repository.","messagePattern":"mission-dir must be inside a git repository\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/autoresearch/contracts.ts","lineNumber":79,"sourceCode":"        ? err.stderr.toString('utf-8').trim()\n        : '';\n    throw contractError(stderr || MISSION_DIR_GIT_ERROR);\n  }\n}\n\nexport function slugifyMissionName(value: string): string {\n  return value\n    .toLowerCase()\n    .replace(/[^a-z0-9]+/g, '-')\n    .replace(/-+/g, '-')\n    .replace(/^-|-$/g, '')\n    .slice(0, 48) || 'mission';\n}\n\nfunction ensurePathInside(parentPath: string, childPath: string): void {\n  const rel = relative(parentPath, childPath);\n  if (rel === '' || (!rel.startsWith('..') && rel !== '..')) return;\n  throw contractError(MISSION_DIR_GIT_ERROR);\n}\n\nfunction extractFrontmatter(content: string): { frontmatter: string; body: string } {\n  const match = content.match(/^---\\r?\\n([\\s\\S]*?)\\r?\\n---\\r?\\n?([\\s\\S]*)$/);\n  if (!match) {\n    throw contractError(SANDBOX_FRONTMATTER_ERROR);\n  }\n  return {\n    frontmatter: match[1] || '',\n    body: (match[2] || '').trim(),\n  };\n}\n\nfunction parseSimpleYamlFrontmatter(frontmatter: string): Record<string, unknown> {\n  const result: Record<string, unknown> = {};\n  let currentSection: string | null = null;\n\n  for (const rawLine of frontmatter.split(/\\r?\\n/)) {","sourceCodeStart":61,"sourceCodeEnd":97,"githubUrl":"https://github.com/Yeachan-Heo/oh-my-codex/blob/3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2/src/autoresearch/contracts.ts#L61-L97","documentation":"ensurePathInside throws MISSION_DIR_GIT_ERROR ('mission-dir must be inside a git repository.') when the mission directory resolved by loadAutoresearchMissionContract is not contained within the repository root reported by git rev-parse --show-toplevel. This guards against mission dirs that escape the repo (absolute paths outside, or on a different filesystem branch).","triggerScenarios":"readGit succeeds but the relative path from repoRoot to missionDir starts with '..' (or equals '..'), e.g. the mission dir is a sibling/parent of the repo root, a symlink pointing outside, or git reports a different toplevel than expected (worktree/submodule mismatch).","commonSituations":"Passing an absolute path to a mission folder that lies outside the repo; symlinks into the repo (git resolves the real toplevel, so the symlinked path computes as outside); mismatched submodule/worktree setups where the reported toplevel differs from the mission dir's location.","solutions":["Move or copy the mission directory inside the git work tree and pass that path.","Avoid symlinked mission dirs; pass the real path inside the repo.","For worktrees/submodules, ensure the mission dir is inside the toplevel that git rev-parse --show-toplevel actually reports.","Check for typos in the mission-dir argument that resolve to a path outside the repo."],"exampleFix":"// before\nawait loadAutoresearchMissionContract('/elsewhere/missions/m1');\n\n// after\nawait loadAutoresearchMissionContract('missions/m1'); // inside the repo root","handlingStrategy":"validation","validationCode":"import { relative, resolve } from 'node:path';\nimport { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nconst execFileAsync = promisify(execFile);\n\nasync function assertMissionInsideRepo(missionDir: string): Promise<void> {\n  const { stdout } = await execFileAsync('git', ['-C', missionDir, 'rev-parse', '--show-toplevel']);\n  const rel = relative(stdout.trim(), resolve(missionDir));\n  if (rel === '' || rel.startsWith('..')) throw new Error('mission dir outside repo');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await loadAutoresearchMissionContract(dir);\n} catch (err) {\n  if ((err as Error).message.includes('inside a git repository')) {\n    // move mission dir into the repo or fix symlink/worktree layout\n  }\n  throw err;\n}","preventionTips":["Always pass mission dirs located under the git work tree.","Avoid symlinked mission directories.","For worktrees/submodules, resolve the real path before passing it."],"tags":["path-validation","git","mission-dir","autoresearch"],"backgroundTag":"path-outside-repository","analyzedSha":"3ad79a8a6fe6e95fdbb8c00e40716fffe4011ce2","analyzedAt":"2026-08-27T22:18:39.783Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}