cairn-ai 0.2.1 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -58,11 +58,12 @@ export async function createReport({
58
58
  runner = run,
59
59
  timeoutMs = DEFAULT_TIMEOUT_MS,
60
60
  localChecker = localExecutableAvailable,
61
+ platform = process.platform,
61
62
  } = {}) {
62
63
  const resolvedRoot = resolve(root);
63
64
  const entries = await collectEntries(resolvedRoot);
64
65
  const detected = detectStacks(entries);
65
- const requirements = buildRequirements(detected, entries);
66
+ const requirements = buildRequirements(detected, entries, commandOk, platform);
66
67
  const results = [];
67
68
  const installRefused = install && !yes;
68
69
 
@@ -72,6 +73,7 @@ export async function createReport({
72
73
  runner,
73
74
  timeoutMs,
74
75
  localChecker,
76
+ platform,
75
77
  });
76
78
  let installed = false;
77
79
  let installDiagnostic = null;
@@ -95,6 +97,7 @@ export async function createReport({
95
97
  runner,
96
98
  timeoutMs,
97
99
  localChecker,
100
+ platform,
98
101
  });
99
102
  }
100
103
  }
@@ -157,22 +160,24 @@ export async function collectEntries(base) {
157
160
 
158
161
  export function detectStacks(paths) {
159
162
  const stacks = new Set();
160
- if (paths.some((path) => path === "package.json")) stacks.add("javascript");
161
- if (paths.some((path) => path === "tsconfig.json" || path.endsWith(".ts") || path.endsWith(".tsx"))) stacks.add("typescript");
162
- if (paths.some((path) => path === "pyproject.toml" || path.endsWith(".py"))) stacks.add("python");
163
- if (paths.some((path) => path === "composer.json" || path.endsWith(".php"))) stacks.add("php");
164
- if (paths.some((path) => path === "pom.xml" || path === "build.gradle" || path.endsWith(".java"))) stacks.add("java");
163
+ if (hasBasename(paths, "package.json")) stacks.add("javascript");
164
+ if (hasBasename(paths, "tsconfig.json") || paths.some((path) => path.endsWith(".ts") || path.endsWith(".tsx"))) stacks.add("typescript");
165
+ if (hasBasename(paths, "pyproject.toml") || paths.some((path) => path.endsWith(".py"))) stacks.add("python");
166
+ if (hasBasename(paths, "composer.json") || paths.some((path) => path.endsWith(".php"))) stacks.add("php");
167
+ if (hasBasename(paths, "pom.xml", "build.gradle", "build.gradle.kts") || paths.some((path) => path.endsWith(".java"))) stacks.add("java");
165
168
  if (paths.some((path) => path.endsWith(".kt") || path.endsWith(".kts"))) stacks.add("kotlin");
166
- if (paths.some((path) => path === "Package.swift" || path.endsWith(".xcodeproj") || path.endsWith(".xcworkspace") || path.endsWith(".swift"))) stacks.add("swift");
167
- if (paths.some((path) => path === "go.mod" || path.endsWith(".go"))) stacks.add("go");
168
- if (paths.some((path) => path === "Cargo.toml" || path.endsWith(".rs"))) stacks.add("rust");
169
+ if (hasBasename(paths, "Package.swift") || paths.some((path) => hasPathSegmentWithSuffix(path, ".xcodeproj", ".xcworkspace") || path.endsWith(".swift"))) stacks.add("swift");
170
+ if (hasBasename(paths, "go.mod") || paths.some((path) => path.endsWith(".go"))) stacks.add("go");
171
+ if (hasBasename(paths, "Cargo.toml") || paths.some((path) => path.endsWith(".rs"))) stacks.add("rust");
169
172
  return [...stacks];
170
173
  }
171
174
 
172
175
  export function buildRequirements(stacks, entries = [], _commandAvailable = commandOk, platform = process.platform) {
173
176
  const requirements = [];
174
177
  if (stacks.includes("javascript")) {
175
- requirements.push(req("node", ["--version"], "JavaScript runtime for package scripts and CLI checks"));
178
+ requirements.push(unsafeReq("node", ["--version"], "JavaScript runtime for package scripts and CLI checks", "automatic runtime installation is unavailable"));
179
+ const manager = packageManager(entries);
180
+ requirements.push(unsafeReq(manager, ["--version"], `JavaScript package manager selected from ${packageManagerSource(entries)}`, "automatic package-manager installation is unavailable"));
176
181
  }
177
182
  if (stacks.includes("typescript")) {
178
183
  requirements.push(unsafeReq("typescript-language-server", ["--version"], "TypeScript LSP server", "automatic package installation is disabled without pinned package versions"));
@@ -183,7 +188,7 @@ export function buildRequirements(stacks, entries = [], _commandAvailable = comm
183
188
  requirements.push(unsafeReq("ruff", ["--version"], "Python lint and format checker", "automatic Python package installation is disabled without pinned package versions"));
184
189
  }
185
190
  if (stacks.includes("php")) {
186
- const composerProject = entries.includes("composer.json");
191
+ const composerProject = hasBasename(entries, "composer.json");
187
192
  requirements.push(req("php", ["--version"], "PHP runtime for Composer scripts and CLI checks"));
188
193
  if (composerProject) requirements.push(req("composer", ["--version"], "PHP package manager for project-local tool installation"));
189
194
  requirements.push(unsafeReq("phpactor", ["--version"], "PHP LSP server", "automatic Composer package installation is disabled without pinned package versions"));
@@ -204,7 +209,7 @@ export function buildRequirements(stacks, entries = [], _commandAvailable = comm
204
209
  if (stacks.includes("swift")) {
205
210
  requirements.push(req("swift", ["--version"], "Swift toolchain for build and test commands"));
206
211
  requirements.push(req("sourcekit-lsp", ["--version"], "Swift LSP server"));
207
- if (entries.some((path) => path.endsWith(".xcodeproj") || path.endsWith(".xcworkspace"))) {
212
+ if (entries.some((path) => hasPathSegmentWithSuffix(path, ".xcodeproj", ".xcworkspace"))) {
208
213
  requirements.push(req("xcodebuild", ["-version"], "Xcode build verification"));
209
214
  }
210
215
  }
@@ -229,13 +234,16 @@ export function addJvmRequirements(requirements) {
229
234
  }
230
235
 
231
236
  export function addJvmBuildToolRequirements(requirements, entries = []) {
232
- if (entries.includes("gradlew.bat")) pushUnique(requirements, req("gradlew.bat", ["--version"], "Gradle wrapper availability", null, { localOnly: true }));
233
- else if (entries.includes("gradlew")) pushUnique(requirements, req("./gradlew", ["--version"], "Gradle wrapper availability", null, { localOnly: true }));
234
- else if (entries.includes("build.gradle") || entries.includes("build.gradle.kts")) pushUnique(requirements, req("gradle", ["--version"], "Gradle availability"));
235
- if (entries.includes("mvnw.cmd")) pushUnique(requirements, req("mvnw.cmd", ["--version"], "Maven wrapper availability", null, { localOnly: true }));
236
- else if (entries.includes("mvnw.bat")) pushUnique(requirements, req("mvnw.bat", ["--version"], "Maven wrapper availability", null, { localOnly: true }));
237
- else if (entries.includes("mvnw")) pushUnique(requirements, req("./mvnw", ["--version"], "Maven wrapper availability", null, { localOnly: true }));
238
- else if (entries.includes("pom.xml")) pushUnique(requirements, req("mvn", ["--version"], "Maven availability"));
237
+ const gradleWindows = findBasename(entries, "gradlew.bat");
238
+ const gradleUnix = findBasename(entries, "gradlew");
239
+ if (gradleWindows) pushUnique(requirements, req(repositoryCommand(gradleWindows), ["--version"], "Gradle wrapper availability", null, { localOnly: true }));
240
+ else if (gradleUnix) pushUnique(requirements, req(repositoryCommand(gradleUnix), ["--version"], "Gradle wrapper availability", null, { localOnly: true }));
241
+ else if (hasBasename(entries, "build.gradle", "build.gradle.kts")) pushUnique(requirements, req("gradle", ["--version"], "Gradle availability"));
242
+ const mavenWindows = findBasename(entries, "mvnw.cmd", "mvnw.bat");
243
+ const mavenUnix = findBasename(entries, "mvnw");
244
+ if (mavenWindows) pushUnique(requirements, req(repositoryCommand(mavenWindows), ["--version"], "Maven wrapper availability", null, { localOnly: true }));
245
+ else if (mavenUnix) pushUnique(requirements, req(repositoryCommand(mavenUnix), ["--version"], "Maven wrapper availability", null, { localOnly: true }));
246
+ else if (hasBasename(entries, "pom.xml")) pushUnique(requirements, req("mvn", ["--version"], "Maven availability"));
239
247
  }
240
248
 
241
249
  export function pushUnique(requirements, requirement) {
@@ -276,10 +284,12 @@ export function unsafeReq(command, args, reason, installUnavailableReason) {
276
284
  }
277
285
 
278
286
  export function packageManager(entries = []) {
279
- if (entries.includes("bun.lockb") || entries.includes("bun.lock")) return "bun";
280
- if (entries.includes("pnpm-lock.yaml")) return "pnpm";
281
- if (entries.includes("yarn.lock")) return "yarn";
282
- return "npm";
287
+ return selectPackageManager(entries).manager;
288
+ }
289
+
290
+ export function packageManagerSource(entries = []) {
291
+ const selected = selectPackageManager(entries);
292
+ return selected.path ?? "the npm default (no lockfile found)";
283
293
  }
284
294
 
285
295
  export function installDev(pm, packages) {
@@ -294,20 +304,24 @@ export function inspectCommand(requirement, {
294
304
  runner = run,
295
305
  timeoutMs = DEFAULT_TIMEOUT_MS,
296
306
  localChecker = localExecutableAvailable,
307
+ platform = process.platform,
297
308
  } = {}) {
298
- const candidates = commandCandidates(requirement.command, root);
309
+ const candidates = commandCandidates(requirement.command, root, platform);
299
310
  const localOnly = requirement.localOnly || isRepositoryCommand(requirement.command, root);
300
311
 
301
312
  if (!localOnly) {
302
- const diagnostic = execute(requirement.command, requirement.args, { root, runner, timeoutMs });
303
- if (diagnostic.status === 0) {
304
- return {
305
- ok: true,
306
- availability: "verified",
307
- source: "system",
308
- candidate: requirement.command,
309
- diagnostic,
310
- };
313
+ let diagnostic = null;
314
+ for (const candidate of systemCommandCandidates(requirement.command, platform)) {
315
+ diagnostic = execute(candidate, requirement.args, { root, runner, timeoutMs });
316
+ if (diagnostic.status === 0) {
317
+ return {
318
+ ok: true,
319
+ availability: "verified",
320
+ source: "system",
321
+ candidate,
322
+ diagnostic,
323
+ };
324
+ }
311
325
  }
312
326
 
313
327
  for (const candidate of candidates.slice(1)) {
@@ -324,7 +338,7 @@ export function inspectCommand(requirement, {
324
338
 
325
339
  return {
326
340
  ok: false,
327
- availability: diagnostic.timedOut ? "timeout" : "missing",
341
+ availability: diagnostic?.timedOut ? "timeout" : "missing",
328
342
  source: null,
329
343
  candidate: null,
330
344
  diagnostic,
@@ -370,6 +384,11 @@ export function commandCandidates(command, root = process.cwd(), platform = proc
370
384
  return [...new Set(candidates.flatMap((candidate) => hasWindowsExecutableExtension(candidate) ? [candidate] : [candidate, ...extensions.map((extension) => `${candidate}${extension}`)]))];
371
385
  }
372
386
 
387
+ export function systemCommandCandidates(command, platform = process.platform) {
388
+ if (platform !== "win32" || hasWindowsExecutableExtension(command) || isAbsolute(command)) return [command];
389
+ return [command, `${command}.cmd`, `${command}.bat`, `${command}.exe`];
390
+ }
391
+
373
392
  export function localExecutableAvailable(command, platform = process.platform) {
374
393
  try {
375
394
  const info = statSync(command);
@@ -479,6 +498,69 @@ function normalizePath(path) {
479
498
  return path.split(sep).join("/");
480
499
  }
481
500
 
501
+ function pathBasename(path) {
502
+ const normalized = normalizePath(path);
503
+ return normalized.slice(normalized.lastIndexOf("/") + 1);
504
+ }
505
+
506
+ function hasBasename(paths, ...names) {
507
+ const expected = new Set(names);
508
+ return paths.some((path) => expected.has(pathBasename(path)));
509
+ }
510
+
511
+ function findBasename(paths, ...names) {
512
+ const expected = new Set(names);
513
+ return [...paths]
514
+ .filter((path) => expected.has(pathBasename(path)))
515
+ .sort(compareRepositoryPaths)[0] ?? null;
516
+ }
517
+
518
+ function hasPathSegmentWithSuffix(path, ...suffixes) {
519
+ return normalizePath(path)
520
+ .split("/")
521
+ .some((segment) => suffixes.some((suffix) => segment.endsWith(suffix)));
522
+ }
523
+
524
+ function repositoryCommand(path) {
525
+ const normalized = normalizePath(path);
526
+ return normalized.startsWith("./") ? normalized : `./${normalized}`;
527
+ }
528
+
529
+ function selectPackageManager(entries) {
530
+ const lockfiles = [
531
+ { manager: "bun", names: new Set(["bun.lock", "bun.lockb"]), priority: 0 },
532
+ { manager: "pnpm", names: new Set(["pnpm-lock.yaml"]), priority: 1 },
533
+ { manager: "yarn", names: new Set(["yarn.lock"]), priority: 2 },
534
+ { manager: "npm", names: new Set(["package-lock.json", "npm-shrinkwrap.json"]), priority: 3 },
535
+ ];
536
+ const candidates = [];
537
+
538
+ for (const path of entries) {
539
+ const normalized = normalizePath(path);
540
+ const name = pathBasename(normalized);
541
+ const match = lockfiles.find(({ names }) => names.has(name));
542
+ if (!match) continue;
543
+ candidates.push({
544
+ manager: match.manager,
545
+ path: normalized,
546
+ depth: normalized.split("/").length - 1,
547
+ priority: match.priority,
548
+ });
549
+ }
550
+
551
+ candidates.sort((left, right) => left.depth - right.depth
552
+ || left.priority - right.priority
553
+ || left.path.localeCompare(right.path));
554
+ return candidates[0] ?? { manager: "npm", path: null };
555
+ }
556
+
557
+ function compareRepositoryPaths(left, right) {
558
+ const leftPath = normalizePath(left);
559
+ const rightPath = normalizePath(right);
560
+ const depth = leftPath.split("/").length - rightPath.split("/").length;
561
+ return depth || leftPath.localeCompare(rightPath);
562
+ }
563
+
482
564
  function hasWindowsExecutableExtension(command) {
483
565
  return /\.(cmd|bat|exe)$/i.test(command);
484
566
  }
package/scripts/cairn.mjs CHANGED
@@ -67,6 +67,7 @@ function isCliEntry() {
67
67
 
68
68
  const planExecutionPolicy = "The user-called/main agent is the orchestrator; implementation edits go to worker subagents whenever subagent tools are available. When the subagent tool provides a progress-reporting channel, subagents report status to the orchestrator when starting work, when deciding or confirming direction, during periodic progress, and when finishing; the orchestrator must immediately relay received status events to the user. If no mid-run reporting channel exists, the orchestrator relays observable events such as assignment, waiting, and final completion. When a delegated subagent finishes, it must provide its final report before leaving; after the orchestrator captures the final report and evidence, the orchestrator must close or release the completed subagent, then review the final report and evidence before marking the work complete. If subagent tools are unavailable, the main agent takes over implementation directly and records that takeover in evidence.";
69
69
  const workExecutionPolicy = "The user-called/main agent orchestrates, while worker subagents perform implementation edits whenever subagent tools are available. When the subagent tool provides a progress-reporting channel, subagents report status to the orchestrator when starting work, when deciding or confirming direction, during periodic progress, and when finishing; the orchestrator must immediately relay received status events to the user. If no mid-run reporting channel exists, the orchestrator relays observable events such as assignment, waiting, and final completion. When a delegated subagent finishes, it must provide its final report before leaving; after the orchestrator captures the final report and evidence, the orchestrator must close or release the completed subagent, then review the final report and evidence before marking the work complete. If subagent tools are unavailable, the main agent takes over implementation directly and records that takeover in evidence.";
70
+ const tokenEfficientWorkPolicy = "Use a test contract for requirements, invariants, boundaries, and failure modes before the minimum implementation. The tool exit code is authoritative. Run goal verify -- <argv> and expand only failures. Inspect package lifecycle scripts: content-producing or unknown scripts must never use --ignore-scripts; only absent or proven content-neutral scripts may use it while prior full-check evidence remains fresh.";
70
71
  const artifactLocalePolicy = "Write user-visible responses and generated or updated documentation, plans, and memory artifacts in the OS locale unless the user asks for another language.";
71
72
  const artifactLocalePolicyKo = "사용자가 다른 언어를 요청하지 않는 한, 사용자에게 보이는 응답과 생성 또는 갱신하는 문서, 계획, 메모리 산출물은 OS locale 언어로 작성합니다.";
72
73
 
@@ -74,56 +75,56 @@ const messages = {
74
75
  en: {
75
76
  memory: `Use the cairn-memory skill. Keep MEMORY.md as a short index and write details to docs/memory/<domain>.md. ${artifactLocalePolicy}`,
76
77
  plan: `Use the cairn-plan skill before non-trivial implementation. Every agent must first read the project-root MEMORY.md for domain knowledge and repository policy. Understand the whole work first, then classify it into executable tasks and sub-tasks. Keep PLAN.md as an index, write detailed plans to docs/plan/<topic>.md, and record Light/Heavy Path triage before any mutation. ${artifactLocalePolicy} ${planExecutionPolicy}`,
77
- work: `Use the cairn-work skill only after a plan exists. Every agent must first read the project-root MEMORY.md for domain knowledge and repository policy. Execute one module task using the recorded Light/Heavy Path route: ${workExecutionPolicy} Capture dry-run/check, module, and surface evidence. ${artifactLocalePolicy} If the user asks a side question while work is active, answer briefly and then resume unless they explicitly ask to pause, stop, or switch tasks.`,
78
+ work: `Use cairn-work only after a plan exists and every agent reads root MEMORY.md. ${tokenEfficientWorkPolicy} Execute one task through its recorded Light/Heavy Path: ${workExecutionPolicy} ${artifactLocalePolicy} Resume active work after side questions unless asked to pause, stop, or switch.`,
78
79
  review: `Use the cairn-review skill. Compare evidence against PLAN.md and MEMORY.md. ${artifactLocalePolicy}`,
79
80
  usage: "Usage: cairn install|upgrade|doctor|uninstall|init|memory|plan|work|review|toolcheck|goal|task",
80
81
  },
81
82
  ko: {
82
83
  memory: `cairn-memory 스킬을 사용하세요. MEMORY.md는 짧은 색인으로, 상세 내용은 docs/memory/<domain>.md에 기록합니다. ${artifactLocalePolicyKo}`,
83
84
  plan: `중요 구현 전에는 cairn-plan 스킬을 사용하세요. 모든 에이전트는 도메인 지식과 저장소 정책을 위해 프로젝트 루트 MEMORY.md를 먼저 읽어야 합니다. 먼저 전체 작업을 파악한 뒤 실행 가능한 task와 필요한 sub-task로 분류합니다. PLAN.md는 색인으로, 상세 계획은 docs/plan/<topic>.md에 기록하며 파일 변경 전 Light/Heavy Path 트리아지를 남깁니다. ${artifactLocalePolicyKo} ${planExecutionPolicy}`,
84
- work: `계획이 있을 때만 cairn-work 스킬을 사용하세요. 모든 에이전트는 도메인 지식과 저장소 정책을 위해 프로젝트 루트 MEMORY.md를 먼저 읽어야 합니다. 기록된 Light/Heavy Path 경로로 하나의 모듈 작업을 실행합니다. ${workExecutionPolicy} dry-run/check, 모듈, 표면 증거를 확보합니다. ${artifactLocalePolicyKo} 작업 중 사용자가 곁가지 질문을 하면 짧게 답한 뒤, 명시적으로 일시중지/중단/작업 전환을 요청한 경우가 아니면 이전 active work를 계속합니다.`,
85
+ work: `계획이 있고 모든 에이전트가 루트 MEMORY.md를 읽은 뒤에만 cairn-work를 사용하세요. ${tokenEfficientWorkPolicy} 기록된 Light/Heavy Path로 한 task를 수행합니다. ${workExecutionPolicy} ${artifactLocalePolicyKo} 곁가지 질문 뒤에는 일시중지·중단·전환 요청이 없으면 active work를 재개합니다.`,
85
86
  review: `cairn-review 스킬을 사용하세요. PLAN.md와 MEMORY.md 기준으로 증거를 비교합니다. ${artifactLocalePolicyKo}`,
86
87
  usage: "사용법: cairn install|upgrade|doctor|uninstall|init|memory|plan|work|review|toolcheck|goal|task",
87
88
  },
88
89
  ja: {
89
90
  memory: `cairn-memory スキルを使用してください。MEMORY.md は短い索引にし、詳細は docs/memory/<domain>.md に記録します。${artifactLocalePolicy}`,
90
91
  plan: `cairn-plan スキルを使用してください。PLAN.md は索引にし、詳細計画は docs/plan/<topic>.md に記録します。${artifactLocalePolicy} ${planExecutionPolicy}`,
91
- work: `cairn-work スキルを使用してください。1つのモジュールスライスを実行し、モジュール証拠と表面証拠を取得します。${workExecutionPolicy} ${artifactLocalePolicy}`,
92
+ work: `cairn-work スキルを使用してください。1つのモジュールスライスを実行し、モジュール証拠と表面証拠を取得します。${tokenEfficientWorkPolicy} ${workExecutionPolicy} ${artifactLocalePolicy}`,
92
93
  review: `cairn-review スキルを使用してください。PLAN.md と MEMORY.md に照らして証拠を比較します。${artifactLocalePolicy}`,
93
94
  usage: "使い方: cairn install|upgrade|doctor|uninstall|init|memory|plan|work|review|toolcheck|goal|task",
94
95
  },
95
96
  zh: {
96
97
  memory: `使用 cairn-memory skill。将 MEMORY.md 保持为简短索引,并将详细内容写入 docs/memory/<domain>.md。${artifactLocalePolicy}`,
97
98
  plan: `使用 cairn-plan skill。将 PLAN.md 保持为索引,并将详细计划写入 docs/plan/<topic>.md。${artifactLocalePolicy} ${planExecutionPolicy}`,
98
- work: `使用 cairn-work skill。执行一个模块切片,并收集模块证据。${workExecutionPolicy} ${artifactLocalePolicy}`,
99
+ work: `使用 cairn-work skill。执行一个模块切片,并收集模块证据。${tokenEfficientWorkPolicy} ${workExecutionPolicy} ${artifactLocalePolicy}`,
99
100
  review: `使用 cairn-review skill。根据 PLAN.md 和 MEMORY.md 对比证据。${artifactLocalePolicy}`,
100
101
  usage: "用法: cairn install|upgrade|doctor|uninstall|init|memory|plan|work|review|toolcheck|goal|task",
101
102
  },
102
103
  es: {
103
104
  memory: `Usa la skill cairn-memory. Mantén MEMORY.md como un índice breve y escribe los detalles en docs/memory/<domain>.md. ${artifactLocalePolicy}`,
104
105
  plan: `Usa la skill cairn-plan. Mantén PLAN.md como índice y escribe los planes detallados en docs/plan/<topic>.md. ${artifactLocalePolicy} ${planExecutionPolicy}`,
105
- work: `Usa la skill cairn-work. Ejecuta un solo segmento de módulo y captura evidencia de módulo y de superficie. ${workExecutionPolicy} ${artifactLocalePolicy}`,
106
+ work: `Usa la skill cairn-work. Ejecuta un solo segmento de módulo y captura evidencia de módulo y de superficie. ${tokenEfficientWorkPolicy} ${workExecutionPolicy} ${artifactLocalePolicy}`,
106
107
  review: `Usa la skill cairn-review. Compara la evidencia con PLAN.md y MEMORY.md. ${artifactLocalePolicy}`,
107
108
  usage: "Uso: cairn install|upgrade|doctor|uninstall|init|memory|plan|work|review|toolcheck|goal|task",
108
109
  },
109
110
  fr: {
110
111
  memory: `Utilisez la skill cairn-memory. Gardez MEMORY.md comme index court et écrivez les détails dans docs/memory/<domain>.md. ${artifactLocalePolicy}`,
111
112
  plan: `Utilisez la skill cairn-plan. Gardez MEMORY.md comme index et écrivez les plans détaillés dans docs/plan/<topic>.md. ${artifactLocalePolicy} ${planExecutionPolicy}`,
112
- work: `Utilisez la skill cairn-work. Exécutez une tranche de module et capturez les preuves de module et de surface. ${workExecutionPolicy} ${artifactLocalePolicy}`,
113
+ work: `Utilisez la skill cairn-work. Exécutez une tranche de module et capturez les preuves de module et de surface. ${tokenEfficientWorkPolicy} ${workExecutionPolicy} ${artifactLocalePolicy}`,
113
114
  review: `Utilisez la skill cairn-review. Comparez les preuves avec PLAN.md et MEMORY.md. ${artifactLocalePolicy}`,
114
115
  usage: "Utilisation: cairn install|upgrade|doctor|uninstall|init|memory|plan|work|review|toolcheck|goal|task",
115
116
  },
116
117
  de: {
117
118
  memory: `Verwenden Sie die Skill cairn-memory. Halten Sie MEMORY.md als kurzen Index und schreiben Sie Details nach docs/memory/<domain>.md. ${artifactLocalePolicy}`,
118
119
  plan: `Verwenden Sie die Skill cairn-plan. Halten Sie MEMORY.md als Index und schreiben Sie Detailpläne nach docs/plan/<topic>.md. ${artifactLocalePolicy} ${planExecutionPolicy}`,
119
- work: `Verwenden Sie die Skill cairn-work. Führen Sie einen Modulabschnitt aus und erfassen Sie Modul- und Oberflächenbelege. ${workExecutionPolicy} ${artifactLocalePolicy}`,
120
+ work: `Verwenden Sie die Skill cairn-work. Führen Sie einen Modulabschnitt aus und erfassen Sie Modul- und Oberflächenbelege. ${tokenEfficientWorkPolicy} ${workExecutionPolicy} ${artifactLocalePolicy}`,
120
121
  review: `Verwenden Sie die Skill cairn-review. Vergleichen Sie Belege mit PLAN.md und MEMORY.md. ${artifactLocalePolicy}`,
121
122
  usage: "Verwendung: cairn install|upgrade|doctor|uninstall|init|memory|plan|work|review|toolcheck|goal|task",
122
123
  },
123
124
  pt: {
124
125
  memory: `Use a skill cairn-memory. Mantenha MEMORY.md como um índice curto e escreva detalhes em docs/memory/<domain>.md. ${artifactLocalePolicy}`,
125
126
  plan: `Use a skill cairn-plan. Mantenha MEMORY.md como índice e escreva planos detalhados em docs/plan/<topic>.md. ${artifactLocalePolicy} ${planExecutionPolicy}`,
126
- work: `Use a skill cairn-work. Execute uma fatia de módulo e capture evidências de módulo e de superfície. ${workExecutionPolicy} ${artifactLocalePolicy}`,
127
+ work: `Use a skill cairn-work. Execute uma fatia de módulo e capture evidências de módulo e de superfície. ${tokenEfficientWorkPolicy} ${workExecutionPolicy} ${artifactLocalePolicy}`,
127
128
  review: `Use a skill cairn-review. Compare evidências com PLAN.md e MEMORY.md. ${artifactLocalePolicy}`,
128
129
  usage: "Uso: cairn install|upgrade|doctor|uninstall|init|memory|plan|work|review|toolcheck|goal|task",
129
130
  },
@@ -0,0 +1,68 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "release": "0.2.2",
4
+ "files": {
5
+ ".agents/workflows/cairn-doctor.md": "cb5525f795ec1b1b3155396b6e175362af0f88ef376f9b92271ef5f7924f9da8",
6
+ ".agents/workflows/cairn-install.md": "036a74fae6df7c7b7b4dc567a3fdb4986fae4ef41c273ad2950a54e57a5e3cfd",
7
+ ".agents/workflows/cairn-memory.md": "af7c94799d0d0878d5386610ba95be9c22a673f75d81e5449053ef4af6bb8c2c",
8
+ ".agents/workflows/cairn-plan.md": "ad6791e3c34cefff161bf9d0d985dd3fe277808c6d3d3e43ffedd132fda756bd",
9
+ ".agents/workflows/cairn-review.md": "4a8698b2680cab3220a2b318eb053ae9b69378f745c0d9084eabd912a65832f5",
10
+ ".agents/workflows/cairn-toolcheck.md": "530051de82b89ba97a5f8bc8951e0c80482849551cb81c6c5dc5dd975c2f0845",
11
+ ".agents/workflows/cairn-uninstall.md": "81f2e1551eff44b97128da0d715fe1b43f14305eb0dc1b06aad4743fae8d2d67",
12
+ ".agents/workflows/cairn-upgrade.md": "0a646846edb989aef7cbce3963721906860f0edc1af55811cc3b4b3ad3ff9f15",
13
+ ".agents/workflows/cairn-work.md": "c508db4dd2f8c00c4439c10153cce5a63a0fcb60f9e3ffa94412f3e64ab1e84b",
14
+ ".claude/agents/explorer.md": "bb7807ebc8a39d1e63b342e0d4e44d24b93ee110d15e322ec60d43c49b9a9d45",
15
+ ".claude/agents/worker.md": "2619eb04fce8dd637cf506ac604f7d41cb8fb13934a2cd4e9ed2df05b95ab30e",
16
+ ".claude/commands/cairn-doctor.md": "1c6b39fe9f7fdbd285018d39677b0e35b9cd5ddb99eb343f85bb5f735efbb733",
17
+ ".claude/commands/cairn-install.md": "52b2b09c0a756112b13da9dd6653fbc84a86b026b24c123b248c80c797dbd77e",
18
+ ".claude/commands/cairn-memory.md": "3da4f95c24b2b12eddf09ef365023a2d1fd79467f8c50dee86e5a860c62bd9fd",
19
+ ".claude/commands/cairn-plan.md": "dfb9d5fb4643158323f3b4e116dfda518384a74ca300b6571929e806a5cdae1f",
20
+ ".claude/commands/cairn-review.md": "c5a8aa682e871c824f07830ef2cc8d6644df12e256393ed1a54739cbc3b0f817",
21
+ ".claude/commands/cairn-toolcheck.md": "169e52144875b79b230fbe9f20343c820b81c4f6a87a5328d9db7155253d819d",
22
+ ".claude/commands/cairn-uninstall.md": "86c46dc88f1f7d236046d996768b91145deacdbc8b5a456044d9696ee1769038",
23
+ ".claude/commands/cairn-upgrade.md": "8b7b82a3d235e0f6a0dbedc393a3ebe5773e88d593bcaa8468ac7bbc653b038d",
24
+ ".claude/commands/cairn-work.md": "6b434795411ffeac53625f3daceefee2edb47030dd9b93d764a539de5c79562b",
25
+ ".codex-plugin/plugin.json": "5d75b04c4240da2235c5489a328ac94450f7d9c49078fba33273d1ca0eb96a30",
26
+ "LICENSE": "1284a0177aecbc8ed521c2c99480f0f6767ab1ca4b832f30d513ae1aa1368f92",
27
+ "README.de.md": "3d73f892136bd1beb15f1e95071b85b74b9e18ef38d8cebde02a09266beadb28",
28
+ "README.es.md": "7bc44d65b47b1c8ee9a7a6a65360bef89a0ed611c1e808d0ed1a4a95108c9271",
29
+ "README.fr.md": "65eb784d27249c50e317708adf75aafb2495e4f552cb0ec3b123999a920abef0",
30
+ "README.ja.md": "241fd360d6ea430494b099872fbe9f64480e22ef26409e7fc43a4eee1fe183ee",
31
+ "README.ko.md": "f39289045e26834ce518ffded3af3f6ac45ca09ec72c86eef6aa1c265c5d7aad",
32
+ "README.md": "2ccb2cab43bad082ccc566cf9330d577e66df283746e37c4d8ab25a935e57473",
33
+ "README.pt.md": "306ccf468c59d2cea50e385c99308aa4d7a952dbfd65c5586cf06faab13e7b5b",
34
+ "README.zh.md": "d788cdccb4fc327b8242085eed45a3e8ac59a0f3c4bf53f51dcae49e0996f578",
35
+ "agents/explorer.md": "21e0306e1184bd0adc9d5339f86f8e8e0125892054142047446c705581cf50d1",
36
+ "agents/worker.md": "6564dc9676687bc8f5a2b62691f4f3a776c0f5580bfa9f9bf3fe1c4831acd377",
37
+ "commands/cairn-doctor.md": "96af34769f64796d1b75837f4124b75f04da4363d80b521d387489d79a0a5ae3",
38
+ "commands/cairn-install.md": "0a6208ad7a3057bf7ee3af49654e009ae96154d25335f78706c9cca5d9482be9",
39
+ "commands/cairn-memory.md": "396e3d3d0863267aef2e9d450f6c664268aa53f3e61cfd759fd9a6d568b98aa5",
40
+ "commands/cairn-plan.md": "278da82240c8d19f11cf83d8da084f8b309443a8736f7e75503250a0a9b16e1e",
41
+ "commands/cairn-review.md": "1ce071e653ed06cfc915cae134aed8bc3391b3563af5bff5531289e292d90596",
42
+ "commands/cairn-toolcheck.md": "b0ce6737f69bb8e138b49b9b412c0cf0e9dc50534c31657eb0936f1513d5572c",
43
+ "commands/cairn-uninstall.md": "bf7005ef29817c19b39a21c4ffc440da0f0e694a0a09355c6e23777a8a291ab5",
44
+ "commands/cairn-upgrade.md": "593d1ceb3af93c94242ccb8de0a2601519072303f463be4961fd803e71e5d880",
45
+ "commands/cairn-work.md": "475de81bbf54c4deccccd7a63969efb20db6654ff34734f93daf95d4a8799e40",
46
+ "docs/model-guidance/README.md": "9291a6ca8a7717aefb39facf0dd085672e30fed2ba02461bd1e27efb0c6d1826",
47
+ "docs/model-guidance/claude.md": "7c8a3ff0e9d639735088d765794d168c43b87a6611dc89a6776a5e840ea2df19",
48
+ "docs/model-guidance/codex.md": "45eb20fcf35b9ecfc0ea7159235081c5afdce983d73174505079d3db8198ab5c",
49
+ "hooks/hooks.json": "5603c6533a461b53b332273ba2172a625fd6ae21d24e1ceb3a1c16862737b098",
50
+ "package.json": "d8e558f53ad4cd7d24a19d119aed27811a9ce88bde70d6423a7b6bd55e4e8802",
51
+ "scripts/cairn": "0df05a9c2480617eebd11d4e6299c3ff56fe3e243801c1fb55ab6a80a9006fe6",
52
+ "scripts/cairn-goal.mjs": "05f7277ba0ffa26ce5674259a6a4d40de01ffdb746645e7e32ff7c680ca214cd",
53
+ "scripts/cairn-lifecycle.mjs": "74b543bbb201514f4120f6de35b5d27643644b6a01f2ebf347c1f767c57f4109",
54
+ "scripts/cairn-paths.mjs": "815a33bc2ad6b29a3cd1c1db867f7c3e36e2913772e64dc53e563a2f9c30d674",
55
+ "scripts/cairn-state.mjs": "199b28e5ba3e9a296279866ba73f9769150537ceb90722b2958a1a0303511379",
56
+ "scripts/cairn-state.sh": "26f817a50a70b79aade3212ba89dbe0ac507998a4ce1565510624623b3899186",
57
+ "scripts/cairn-toolcheck.mjs": "141ec6572f81f9bf82b3e69b4b074e877ffa6166722faf3dd5b0a003a44e431d",
58
+ "scripts/cairn.mjs": "935048771c7393a0f5900ebc1a0501b0cfe2b63703febe9135c1fba541ca5169",
59
+ "skills/cairn-memory/SKILL.md": "0dcf32ceee7133df2889ea33e1b0856a0d5eedfd6d66ec43835c6b68846b66d1",
60
+ "skills/cairn-plan/SKILL.md": "a6997738a0311d0b9f40cf150e768f879af2577cbc4194595ed267fd42a91ac4",
61
+ "skills/cairn-review/SKILL.md": "52d2d2c3b96d6341f15350255acffa83e54ca3669a88f1c927811accb06f6a6a",
62
+ "skills/cairn-work/SKILL.md": "a20e47bb7cee345c526d619787e6ce054ea96806a51e8815a4329440237592be",
63
+ "templates/MEMORY.md": "a7791c4d400d3acec0dd813709c1dd8362ffc2ed2f9c407730b807b9370bdd75",
64
+ "templates/PLAN.md": "66a53a8f347949761cadfb95677f2e2ffca4e9dcc7b7129cdd70693cc558e00b",
65
+ "templates/domain-memory.md": "4a22556739a6068694c23eef723a6c40e41855884d420ed92623c3a5af42de49",
66
+ "templates/work-plan.md": "40458e17fbd1f8c307ba07638612131f996f711fd100d31e3fca06eeb8a8edb9"
67
+ }
68
+ }
@@ -23,27 +23,33 @@ Resolve Cairn's read-only runtime from `references/cairn-runtime.json` next to t
23
23
 
24
24
  Use logical resource IDs such as `cairn://templates/work-plan.md` and `cairn://docs/model-guidance/codex.md` in repository plans. Resolve those IDs through `pluginRoot` only while executing. Keep `repoRoot` separate and pass it explicitly as `--root <repoRoot>`.
25
25
 
26
- For an implementation or continued-execution request, treat the request itself as authorization to define stable, ordered task steps and activate the decision-complete plan with the installed CLI, even when the user does not mention a goal:
26
+ For an implementation or continued-execution request, treat the request itself as authorization to create an initial two-phase plan and activate its roadmap, even when the user does not mention a goal. The initial plan is decision-complete for the `triage-plan` task but is not implementation-ready. After writing it and its `PLAN.md` index entry, synchronize the same ordered steps to Codex UI tools when available (`update_plan` first, then `create_goal`) and start the repository goal before exploration, toolcheck, complexity triage, or a non-blocking user question:
27
27
 
28
28
  ```sh
29
- node "<pluginRoot>/scripts/cairn.mjs" goal start --root "<repoRoot>" --goal "<objective>" --plan "docs/plan/<topic>.md" --tasks '[{"id":"task-1","title":"<task>"}]' --criteria "<completion criterion>" --session "<current session_id>"
29
+ node "<pluginRoot>/scripts/cairn.mjs" goal start --root "<repoRoot>" --goal "<objective>" --plan "docs/plan/<topic>.md" --tasks '[{"id":"triage-plan","title":"Run planned triage and finalize the plan","requiredEvidence":["planArtifact","triageDecision"]},{"id":"implement","title":"Implement the finalized plan"},{"id":"verify","title":"Verify and review the result"}]' --criteria "<completion criterion>" --session "<current session_id>"
30
30
  ```
31
31
 
32
- Use the current Codex hook `session_id` when it is available. If the surface does not expose a stable session ID, omit `--session` and record that the goal is repository-scoped.
32
+ Use the current Codex hook `session_id` when it is available. If the surface does not expose a stable session ID, omit `--session` and record that the goal is repository-scoped. If Codex UI plan/goal tools are unavailable, record that limitation and continue with the repository plan and goal; never claim UI synchronization that did not occur.
33
33
 
34
34
  ## Procedure
35
35
 
36
36
  1. Run `node "<pluginRoot>/scripts/cairn.mjs" init --root "<repoRoot>"` through the resolved runtime. If the runtime is unavailable, ensure `MEMORY.md`, `PLAN.md`, `docs/memory`, and `docs/plan` directly and report that `cairn doctor` or `cairn upgrade` is required.
37
37
  2. Read the project-root `MEMORY.md` before repository exploration, tool selection, or delegation.
38
- 3. Read only the relevant `docs/memory/*.md` files referenced by `MEMORY.md` or required by the current task.
39
- 4. Based on the active or assigned model, read `cairn://docs/model-guidance/README.md` and only the relevant installed model guidance.
40
- 5. Run `node "<pluginRoot>/scripts/cairn.mjs" toolcheck --root "<repoRoot>"` to identify repository stacks and required tools.
38
+ 3. Classify the request as implementation/continued execution versus consultation/explanation/plan-only. For implementation/continued execution, immediately create an initial `docs/plan/<topic>.md` from `cairn://templates/work-plan.md` and add its `PLAN.md` index entry. Based only on the request and `MEMORY.md`, describe the observable outcome, anticipated affected surfaces, and stable top-level roadmap. Make `triage-plan` the first active task and specify its memory inputs, investigation scope, toolcheck, Heavy Path criteria, and exact condition for updating the plan. Mark the plan `initial: decision-complete for triage; not implementation-ready`.
39
+ 4. Before reading detailed memory, toolcheck, repository exploration, complexity triage, delegation, or any non-blocking user question, synchronize the initial roadmap:
40
+ - Call Codex `update_plan` first when available so the UI displays the ordered tasks with `triage-plan` in progress.
41
+ - Call Codex `create_goal` next when available so the UI displays the active objective. The implementation request plus this developer-level skill instruction is authorization; do not use it for consultation, explanation, or plan-only requests.
42
+ - Start or attach the repository Cairn goal with the same objective, plan ID, stable task IDs, and current session ID.
43
+ - If a UI tool is unavailable, record the limitation in the plan and continue with repository state.
44
+ 5. Execute the planned `triage-plan` task: read only the relevant `docs/memory/*.md`, read applicable installed model guidance, run toolcheck, and explore the repository with focused search and LSP/symbol tools.
41
45
  6. Treat tool installation as a separate external-state change. Do not install during planning without explicit user approval. After approval, use `toolcheck --install --yes --root "<repoRoot>"` only for pinned/supported installers, or the approved repository-native command. Otherwise record the missing tool and proposed command as a blocker.
42
- 7. Explore the repository with focused search and LSP/symbol tools after tool readiness is confirmed.
43
- 8. Summarize the whole work and affected surfaces, then classify the plan into small executable tasks. Use sub-tasks only when a task needs further breakdown to be verifiable.
44
- 9. Identify the closest available dry-run or check mode for every task that can mutate external state.
45
- 10. Run complexity triage before applying workflow guidance from agents, plugins, or delegated roles. Record the route before any implementation task.
46
- 11. Delegate according to the selected path and model guidance instead of asking the user.
46
+ 7. Summarize the whole work and affected surfaces, classify it into small executable tasks/sub-tasks, identify dry-run/check modes, and run complexity triage before applying workflow guidance from agents, plugins, or delegated roles.
47
+ 8. Update the same repository plan with the selected route, all checked Heavy Path signals, concrete file scope, tool readiness, delegation, verification commands, and fail-closed evidence. Mark it `decision-complete for implementation`, then call Codex `update_plan` again so the UI matches the finalized roadmap.
48
+ - Spend planning tokens on executable test design: requirements, invariants, boundaries, failure modes, expected initial failure, and authoritative tool result.
49
+ - Give implementation a bounded test contract and exact file scope; require the minimum passing change rather than renewed broad discovery.
50
+ - Plan focused tests first, one final full check, and a package dry-run when no change occurred after the full check. Require lifecycle inspection, normal script execution for content-producing or unknown scripts, and `--ignore-scripts` only for absent or proven content-neutral scripts. Mark evidence stale after relevant mutation.
51
+ 9. Only after step 8, record bound `planArtifact` and `triageDecision` evidence records, complete the `triage-plan` task, and advance to implementation.
52
+ 10. Delegate according to the selected path and model guidance instead of asking the user.
47
53
  - Use `explorer` for independent read-only codebase discovery, impact analysis, pattern searches, and read-only verification that can run in parallel when the current tool list supports it.
48
54
  - Treat the user-called/main agent as the orchestrator: it plans, assigns, verifies, and records evidence.
49
55
  - Use `worker` for actual implementation edits with clear file ownership whenever subagent tools are available, on both Light Path and Heavy Path.
@@ -53,10 +59,9 @@ Use the current Codex hook `session_id` when it is available. If the surface doe
53
59
  - If subagent tools are unavailable, the main agent takes over implementation directly and records that takeover in evidence; do not stop before local implementation solely because subagent tools are unavailable.
54
60
  - Keep urgent non-implementation blocking work local when the next step depends immediately on the result.
55
61
  - Tell every delegated agent and child subagent to read the project-root `MEMORY.md` before work, keep scope, and tell workers they are not alone in the codebase and must not revert others' edits.
56
- 12. Create `docs/plan/<topic>.md` from `cairn://templates/work-plan.md` resolved through the installed runtime.
57
- 13. Add a short index entry to `PLAN.md`.
58
- 14. Give every plan and ordered task step a stable ID and declare task-level and goal-level required evidence. When the user asked for implementation or continuing execution rather than a consultation, explanation, or plan-only result, treat that request as authorization and start or attach the repository Cairn goal after the plan is decision-complete, even if the user never mentioned a goal. The task order must be sufficient for hook context to show the roadmap and restore the current task after side questions.
59
- 15. Write user-visible responses and generated or updated documentation, plans, and memory artifacts in the OS locale unless the user asks for another language.
62
+ 11. Give every plan and ordered task step a stable ID and declare task-level and goal-level required evidence. The task order must be sufficient for both Codex UI and hook context to show the roadmap and restore the current task after side questions.
63
+ 12. Advance a Codex UI step only after the matching repository Cairn task transition succeeds with all required bound evidence records. The repository state is the fail-closed transition authority; UI state is its synchronized projection.
64
+ 13. Write user-visible responses and generated or updated documentation, plans, and memory artifacts in the OS locale unless the user asks for another language.
60
65
 
61
66
  ## Complexity Triage
62
67
 
@@ -116,7 +121,8 @@ Heavy Path flow: plan -> pre-implementation review -> bounded `worker` implement
116
121
 
117
122
  ## Planning Rules
118
123
 
119
- - Every plan must be decision-complete.
124
+ - Every initial plan must be decision-complete for its triage task and explicitly not implementation-ready; the post-triage revision must be decision-complete for implementation.
125
+ - Every implementation or continued-execution plan must be shown through Codex `update_plan` and `create_goal` before triage when those tools are available, then synchronized again after triage. Repository plan/goal persistence remains required on every surface.
120
126
  - Every plan must describe the whole work first, then classify it into executable tasks and any needed sub-tasks.
121
127
  - Every plan must treat the user-called/main agent as the orchestrator and assign actual implementation edits to `worker` whenever subagent tools are available, regardless of Light Path or Heavy Path.
122
128
  - Every plan must require subagents to report status when the subagent tool provides a progress-reporting channel. If no mid-run reporting channel exists, the orchestrator must relay observable events such as assignment, waiting, and final completion.
@@ -152,7 +158,7 @@ Do not silently skip LSP, typecheck, lint, dry-run, or verification because the
152
158
  Prefer repository-native dry-run and check modes before mutating external state.
153
159
 
154
160
  - Migrations and database changes: `--pretend`, dry-run, schema diff, rollback feasibility check, or equivalent.
155
- - Package and release work: `npm pack --dry-run`, publish dry-run, build check, or equivalent.
161
+ - Package and release work: inspect lifecycle scripts first, then use normal `npm pack --dry-run`, publish dry-run, build check, or equivalent. Never skip content-producing or unknown lifecycle scripts with `--ignore-scripts`; it is allowed only for absent or proven content-neutral scripts while prior full-check evidence remains fresh.
156
162
  - Infrastructure and deployment: plan, diff, validate, check, or equivalent.
157
163
  - Code generation and formatting: check mode before write mode when available.
158
164
  - If no dry-run mode exists, record that fact and select the smallest reversible command or test artifact available.
@@ -48,5 +48,5 @@ CONTEXT: docs/plan/<topic>.md, MEMORY.md, relevant docs/memory notes, changed fi
48
48
  - Dry-run or check evidence is present when required, or the plan records why no dry-run exists.
49
49
  - Failed gates followed the bounded loop policy and did not continue past two verification passes without a recorded blocker.
50
50
  - Review evidence exists in `docs/plan/<topic>.md`.
51
- - Goal-level final review evidence is stored as a successful structured receipt before the goal is marked `completed`.
51
+ - Goal-level final review evidence is stored as a successful structured evidence record before the goal is marked `completed`.
52
52
  - The next agent can understand verification without rereading the full conversation.
@@ -31,16 +31,37 @@ Use the installed CLI for durable state. Important forms are:
31
31
 
32
32
  ```sh
33
33
  node "<pluginRoot>/scripts/cairn.mjs" goal status --root "<repoRoot>"
34
- node "<pluginRoot>/scripts/cairn.mjs" goal assign --root "<repoRoot>" --task "<task-id>" --agent "<agent-id>"
35
- node "<pluginRoot>/scripts/cairn.mjs" goal receipt --root "<repoRoot>" --task "<task-id>" --kind moduleAcceptance --command "<verification command>" --exit 0
36
- node "<pluginRoot>/scripts/cairn.mjs" goal receipt --root "<repoRoot>" --task "<task-id>" --kind surfaceIntegration --command "<verification command or QA artifact>" --exit 0
37
- node "<pluginRoot>/scripts/cairn.mjs" goal task --root "<repoRoot>" --task "<task-id>" --status completed
38
- node "<pluginRoot>/scripts/cairn.mjs" goal receipt --root "<repoRoot>" --scope goal --kind finalReview --command "<review command>" --exit 0
39
- node "<pluginRoot>/scripts/cairn.mjs" goal complete --root "<repoRoot>"
34
+ node "<pluginRoot>/scripts/cairn.mjs" goal assign --quiet --root "<repoRoot>" --task "<task-id>" --agent "<agent-id>"
35
+ node "<pluginRoot>/scripts/cairn.mjs" goal verify --quiet --root "<repoRoot>" --task "<task-id>" --kind moduleAcceptance --watch "<path|path>" -- <tool> <arg> ...
36
+ node "<pluginRoot>/scripts/cairn.mjs" goal verify --quiet --root "<repoRoot>" --task "<task-id>" --kind surfaceIntegration --watch "<path|path>" -- <tool> <arg> ...
37
+ node "<pluginRoot>/scripts/cairn.mjs" goal task --quiet --root "<repoRoot>" --task "<task-id>" --status completed
38
+ node "<pluginRoot>/scripts/cairn.mjs" goal verify --quiet --root "<repoRoot>" --scope goal --kind finalReview --watch "<path|path>" -- <tool> <arg> ...
39
+ node "<pluginRoot>/scripts/cairn.mjs" goal complete --quiet --root "<repoRoot>"
40
40
  ```
41
41
 
42
+ `goal verify` executes the argv after `--` without a shell and records its exit code, bounded tool output, output digest, and watched-workspace fingerprint. The legacy `goal receipt` command imports declared evidence for compatibility; declared evidence does not satisfy a default tool-bound goal.
43
+
44
+ Verification defaults to 600,000 ms (10 minutes), accepts a shorter `--timeout-ms`, and is capped at 3,600,000 ms (1 hour). The external command never holds the state lock. A receipt is committed only when the starting goal/task identity and the pre/post watched fingerprint still match. Choose the watch set and verification command at the semantic boundary that proves the claimed behavior.
45
+
42
46
  Use `goal pause`, `goal block --reason "<concrete blocker>"`, or `goal cancel` when that is the truthful state. A task can be blocked with `goal task --task "<task-id>" --status blocked --reason "<concrete blocker>"`.
43
47
 
48
+ ## Token-Efficient Test Contract
49
+
50
+ Use the test contract first. Spend reasoning on requirements, invariants, boundaries, and failure modes before implementation. Turn them into focused executable tests, confirm that new tests fail for the intended reason, then make the minimum implementation that passes.
51
+
52
+ Treat the tool exit code and bounded machine summary as authoritative. On success, retain only the command, exit code, and pass count needed for evidence. Expand diagnostics and repository context only around the failing test; do not reread unrelated files or replay successful output.
53
+
54
+ Use this verification ladder:
55
+
56
+ 1. Run the focused contract tests.
57
+ 2. Implement the minimum change and rerun only those tests while diagnosing.
58
+ 3. Run the full repository check once after the final change.
59
+ 4. Before package verification, inspect package lifecycle scripts such as `prepack`, `prepare`, and `prepublishOnly` and classify whether they produce package contents.
60
+ 5. Run normal `npm pack --dry-run` by default. Content-producing or unknown lifecycle scripts must never be skipped with `--ignore-scripts`; their generated contents are part of the package contract.
61
+ 6. Only when lifecycle scripts are absent or proven content-neutral, the full check is still fresh, and the dry-run is only checking the file list may package verification use `npm pack --dry-run --ignore-scripts`.
62
+
63
+ Any relevant mutation after a gate makes that result stale evidence. Rerun the affected focused gate and, when integration could change, the full gate. Use `--quiet` for successful Cairn state mutations so full state JSON does not consume conversation tokens. `goal status` is read-only and always returns state, including when `--quiet` is supplied.
64
+
44
65
  ## Procedure
45
66
 
46
67
  1. Read the project-root `MEMORY.md` first.
@@ -54,15 +75,17 @@ Use `goal pause`, `goal block --reason "<concrete blocker>"`, or `goal cancel` w
54
75
  9. For Heavy Path, follow the full planning and review pipeline recorded in the plan. Run pre-implementation review before mutation and read-only review after evidence exists.
55
76
  10. Delegate implementation to `worker` with exact files, ownership, constraints, and model-specific adjustment rules on both Light Path and Heavy Path whenever subagent tools are available. Tell every delegated agent and child subagent to read the project-root `MEMORY.md` before work, keep scope, preserve others' edits, and report status when the subagent tool provides a progress-reporting channel. Immediately relay received status events to the user. If no mid-run reporting channel exists, relay observable events such as assignment, waiting, and final completion. Require delegated subagents to provide a final report before leaving; after capturing final report and evidence, close or release the completed subagent, then review the final report and evidence before marking the work complete. Use `explorer` for parallel read-only discovery or verification when available and useful. Allow recursive delegation only for bounded sub-tasks when the current surface supports it. If subagent tools are unavailable, the main agent takes over implementation directly and records that takeover in evidence.
56
77
  11. Before mutating external state, run the task dry-run or check command when one is recorded. If none exists, record the absence and continue with the smallest reversible command available.
57
- 12. For Heavy Path, run the recorded automated test command and record explicit `Tests:` evidence before claiming completion.
58
- 13. `worker` must return a structured handoff for only its assigned goal/task with changed files, tool readiness result, dry-run or check result when applicable, automated test result for Heavy Path, module acceptance command and result, surface integration command or QA artifact, blocker, and cleanup notes. A worker must not select the next task.
59
- 14. Re-run both verification gates directly. Record each successful command as a structured Cairn receipt bound to the current goal ID, task ID, and plan.
60
- 15. Apply the bounded loop policy when a gate fails. After two failed passes, transition the task or goal to `blocked` with a concrete blocker instead of continuing automatically.
61
- 16. Mark a task complete only after every required receipt passes. Let Cairn advance to the next pending task, then continue the goal without yielding a false completion.
62
- 17. After all tasks complete, run and record the goal-level final review receipt and explicitly complete the goal. `active` is not a completion state; `paused`, `blocked`, and `cancelled` are allowed terminal-for-now states that stop automatic continuation.
63
- 18. Record evidence in the plan file and state before updating `PLAN.md`.
64
- 19. If the user asks a side question, status question, or narrow clarification while this task is still active, answer it briefly and then resume the previous active work unless the user explicitly asks to pause, stop, or switch tasks.
65
- 20. Write user-visible responses and generated or updated documentation, plans, and memory artifacts in the OS locale unless the user asks for another language.
78
+ 12. Design the focused executable test contract before implementation. For behavior changes, run it and confirm the intended failure; for policy-only work, use the closest deterministic assertion or record why a failing test is unavailable.
79
+ 13. Give the implementation worker only the contract, failing evidence, exact file scope, and constraints; require the minimum implementation rather than renewed broad discovery.
80
+ 14. For Heavy Path, run the recorded automated test command and record explicit `Tests:` evidence before claiming completion.
81
+ 15. `worker` must return a structured handoff for only its assigned goal/task with changed files, tool readiness result, dry-run or check result when applicable, automated test result for Heavy Path, module acceptance command and result, surface integration command or QA artifact, blocker, and cleanup notes. A worker must not select the next task.
82
+ 16. Re-run both verification gates through `goal verify -- ...`. Treat tool exit codes as authoritative, keep successful output summarized, and expand context only for a failing test. Record each success as evidence bound to the current goal ID, task ID, plan, exact argv, and watched-workspace fingerprint.
83
+ 17. Apply the bounded loop policy when a gate fails. After two failed passes, transition the task or goal to `blocked` with a concrete blocker instead of continuing automatically.
84
+ 18. Mark a task complete only after every required evidence record passes. Let Cairn advance to the next pending task, then continue the goal without yielding a false completion.
85
+ 19. After all tasks complete, run and record goal-level final review evidence and explicitly complete the goal. `active` is not a completion state; `paused`, `blocked`, and `cancelled` are allowed terminal-for-now states that stop automatic continuation.
86
+ 20. Record evidence in the plan file and state before updating `PLAN.md`.
87
+ 21. If the user asks a side question, status question, or narrow clarification while this task is still active, answer it briefly and then resume the previous active work unless the user explicitly asks to pause, stop, or switch tasks.
88
+ 22. Write user-visible responses and generated or updated documentation, plans, and memory artifacts in the OS locale unless the user asks for another language.
66
89
 
67
90
  ## Builder Prompt Format
68
91
 
@@ -94,7 +117,7 @@ CONTEXT: docs/plan/<topic>.md task, relevant docs/memory notes, applied model gu
94
117
  Run the closest available dry-run or check mode before commands that mutate external state.
95
118
 
96
119
  - Migrations and database changes: `--pretend`, dry-run, schema diff, rollback feasibility check, or equivalent.
97
- - Package and release work: `npm pack --dry-run`, publish dry-run, build check, or equivalent.
120
+ - Package and release work: inspect lifecycle scripts, then run normal `npm pack --dry-run`, publish dry-run, build check, or equivalent. Use `--ignore-scripts` only for absent or proven content-neutral lifecycle scripts while prior full-check evidence remains fresh.
98
121
  - Infrastructure and deployment: plan, diff, validate, check, or equivalent.
99
122
  - Code generation and formatting: check mode before write mode when available.
100
123
  - If no dry-run mode exists, record that fact in `docs/plan/<topic>.md` before continuing.