cairn-ai 0.4.0 → 0.5.0

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.
@@ -14,7 +14,7 @@ Procedure:
14
14
 
15
15
  1. Every agent reads project-root `MEMORY.md` when present and continues without repository memory when absent before repository exploration, tool selection, work, or delegation.
16
16
  2. For non-trivial implementation or continuation of planned work, write an initial `docs/plan/<topic>.md` and `PLAN.md` entry with `triage-plan` as the first active task. This initial plan is decision-complete for triage and explicitly not implementation-ready.
17
- 3. Before exploration, toolcheck, triage, or delegation, call Codex `update_plan` and then `create_goal` when available, and start the repository Cairn goal with the same roadmap. Copy the session-start hook's `Cairn CLI:` prefix literally into each command; never store it in a shell variable. `--root` may be omitted when the cwd is inside the repository. Fold new work into an unfinished goal with `goal replan` instead of cancelling it; when a hook reports another session owns the goal you continue, run `goal claim --session <id>`.
17
+ 3. Before exploration, toolcheck, triage, or delegation, call Codex `update_plan` and then `create_goal` when available (main agent only; subagents skip), and start the repository Cairn goal with the same roadmap. Copy the session-start hook's `Cairn CLI:` prefix literally into each command; never store it in a shell variable. `--root` may be omitted when the cwd is inside the repository. Fold new work into an unfinished goal with `goal replan` instead of cancelling it; when a hook reports another session owns the goal you continue, run `goal claim --session <id>`.
18
18
  4. Read relevant `docs/memory/*.md` and, under the locator's `resources.modelGuidance`, `claude.md` for Claude-family or `codex.md` for Codex-family models; invoke Node with the locator's absolute `entrypoints.cli`, `toolcheck`, and `--root` for the target repository (installation only with explicit approval), and explore before asking the user.
19
19
  5. Classify the whole work into executable tasks with `dependsOn`, `writeScope`, and handoff files, finalize triage, and update the same plan and `update_plan` roadmap to a decision-complete implementation plan: selected path, checked Heavy Path signals, tool readiness, file scope, dry-run/check, module acceptance, surface integration, stable IDs, and fail-closed evidence. Heavy Path adds an explicit automated test command and `Tests:` evidence line.
20
20
  6. Resolve the skill's reference files through the locator's `pluginRoot` and read them only when needed: `cairn://skills/cairn-plan/planning-rules.md` when finalizing, `cairn://skills/cairn-plan/delegation.md` before dispatching, `cairn://skills/cairn-plan/heavy-path.md` only on a Heavy signal or route change.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cairn",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Token-efficient agent harness with tool readiness for Codex, Claude Code, and Antigravity.",
5
5
  "author": {
6
6
  "name": "WkNam"
package/agents/worker.md CHANGED
@@ -31,6 +31,8 @@ Before the first edit, inspect the assigned exact files, callers, and tests and
31
31
 
32
32
  Read the task's `Requested model` and `Effective model` too: model is chosen per task (fast `haiku`, balanced `sonnet`, stronger `opus`, strongest `fable`). Read the task's `Requested reasoning effort` and `Effective reasoning effort`: Requested is Cairn's desired profile; Effective is what the dispatch response confirms. Every child task/worker you dispatch MUST receive the requested model and effort through the host-exposed task/subagent reasoning-effort option or host-native equivalent (Claude Code: Agent `model`/`effort`, never `fork`); recording without passing is a defect. Report Effective as the requested value when passed and not rejected, `not-applied: <reason>` when not passed, and `inherited` only when the host exposes no effort option. Describe capability only from the exposed interface or dispatch response, distinguish Cairn non-integration from host non-support, and do not alter global/user config.
33
33
 
34
+ Include the exact model ID from your own system prompt in the final report (`unreported` if not stated; never guess). A dispatch that changes a child's model or effort MUST be a new dispatch (Claude Code: a new Agent call), never `SendMessage`. Host task-list tools (`update_plan`, `create_goal`, `wrap_up`) are called by the main agent only; skip them and report in your final report.
35
+
34
36
  Use this prompt format:
35
37
 
36
38
  ```text
@@ -14,7 +14,7 @@ Procedure:
14
14
 
15
15
  1. Every agent reads project-root `MEMORY.md` when present and continues without repository memory when absent before repository exploration, tool selection, work, or delegation.
16
16
  2. For non-trivial implementation or continuation of planned work, write an initial `docs/plan/<topic>.md` and `PLAN.md` entry with `triage-plan` as the first active task. This initial plan is decision-complete for triage and explicitly not implementation-ready.
17
- 3. Before exploration, toolcheck, triage, or delegation, call Codex `update_plan` and then `create_goal` when available, and start the repository Cairn goal with the same roadmap. Copy the session-start hook's `Cairn CLI:` prefix literally into each command; never store it in a shell variable. `--root` may be omitted when the cwd is inside the repository. Fold new work into an unfinished goal with `goal replan` instead of cancelling it; when a hook reports another session owns the goal you continue, run `goal claim --session <id>`.
17
+ 3. Before exploration, toolcheck, triage, or delegation, call Codex `update_plan` and then `create_goal` when available (main agent only; subagents skip), and start the repository Cairn goal with the same roadmap. Copy the session-start hook's `Cairn CLI:` prefix literally into each command; never store it in a shell variable. `--root` may be omitted when the cwd is inside the repository. Fold new work into an unfinished goal with `goal replan` instead of cancelling it; when a hook reports another session owns the goal you continue, run `goal claim --session <id>`.
18
18
  4. Read relevant `docs/memory/*.md` and the installed model guidance, run `toolcheck --root <repoRoot>` (installation only with explicit approval), and explore before asking the user.
19
19
  5. Classify the whole work into executable tasks with `dependsOn`, `writeScope`, and handoff files, finalize triage, and update the same plan and `update_plan` roadmap to a decision-complete implementation plan: selected path, checked Heavy Path signals, tool readiness, file scope, dry-run/check, module acceptance, surface integration, stable IDs, and fail-closed evidence. Heavy Path adds an explicit automated test command and `Tests:` evidence line.
20
20
  6. Read the skill's reference files only when needed: `cairn://skills/cairn-plan/planning-rules.md` when finalizing, `cairn://skills/cairn-plan/delegation.md` before dispatching, `cairn://skills/cairn-plan/heavy-path.md` only on a Heavy signal or route change.
@@ -50,8 +50,8 @@ The goal is not to make prompts long by model. The goal is to stabilize the same
50
50
 
51
51
  ## Model and Reasoning Effort Routing
52
52
 
53
- - The plan decides each task's model from its nature and records `Requested model` and `Effective model`; models are not always inherited. Tiers: fast = read-only exploration, search, inventory, summarization (Claude Code `haiku`); balanced = Light Path implementation and routine verification (`sonnet`); stronger = planning, complexity triage, Heavy Path implementation, review, and final verification/review (`opus`); strongest = Heavy Path design only (`fable`). Antigravity `invoke_subagent` `Model` accepts only a tier enum (fast `flash_lite`, balanced `flash`, stronger `pro`, strongest `pro`; no tier above `pro`) and has no effort option, so Effective reasoning effort is `inherited`. Codex slugs: fast `gpt-6-luna`, balanced `gpt-6.1-terra`, stronger `gpt-6-sol`, strongest `gpt-6-astra`.
54
- - Model Effective: passed and not rejected is that value; not passed is `not-applied: <reason>`; `inherited` only when the host has no model option. If a lower-tier worker fails a gate, re-dispatch that task one tier up (fast, balanced, stronger, strongest) before recording a blocker.
53
+ - The plan decides each task's model from its nature and records `Requested model` and `Effective model`; models are not always inherited. Tiers: fast = read-only exploration, search, inventory, summarization (Claude Code `haiku`); balanced = Light Path implementation and routine verification (`sonnet`); stronger = planning, complexity triage, Heavy Path implementation, review, and final verification/review (`opus`); strongest = Heavy Path design only (`fable`), run as a separate `kind: "design"` task dispatched to that tier rather than part of planning. Antigravity `invoke_subagent` `Model` accepts only a tier enum (fast `flash_lite`, balanced `flash`, stronger `pro`, strongest `pro`; no tier above `pro`) and has no effort option, so Effective reasoning effort is `inherited`. Codex slugs: fast `gpt-6-luna`, balanced `gpt-6.1-terra`, stronger `gpt-6-sol`, strongest `gpt-6-astra`.
54
+ - Model Effective: the passed value plus the exact model ID the worker reports from its own system prompt (e.g. `fable (claude-fable-5-1)`; `<value> (unreported)` when absent, never guess; `goal assign --effective-model <id|unreported>`); not passed is `not-applied: <reason>`; `inherited` only when the host has no model option. If a lower-tier worker fails a gate, re-dispatch that task one tier up (fast, balanced, stronger, strongest) before recording a blocker. A subagent's model and effort are fixed at dispatch; any re-dispatch that changes either (one tier up, Light → Heavy promotion, route-change recalculation) MUST be a new dispatch (Claude Code: a new Agent call), never `SendMessage` to the existing subagent. A design model that is not the host's strongest tier (`--host claude-code` `fable`, `codex` `gpt-6-astra`, `antigravity` `pro`; any of the three when `--host` is omitted) fails completion unless `--allow-no-design-model <reason>`.
55
55
  - Routing is per dispatch only; never change model or global/user config files. `Model routing: inherited` in `MEMORY.md` or the plan disables per-task selection.
56
56
  - Light Path planning, implementation, and verification request `medium`.
57
57
  - Heavy Path planning, review, and implementation request `high`; final verification and review request `xhigh`.
@@ -18,7 +18,7 @@ Claude-family models are strong at long-context retention, policy interpretation
18
18
  - Do not skip the two verification gates even on Light Path.
19
19
  - Do not approve a plan that lacks dry-run or check evidence for external-state-changing work.
20
20
  - Do not allow open-ended verification loops. Require a blocker or sub-task split after two failed passes.
21
- - Choose the model per task (fast `haiku`, balanced `sonnet`, stronger `opus`: planning, triage, Heavy implementation, review, final verification/review; strongest `fable`: Heavy design only) and record `Requested model`/`Effective model`; Effective is the passed value, `not-applied: <reason>`, or `inherited` when the host has no model option. Re-dispatch one tier up (fast, balanced, stronger, strongest) if a lower-tier worker fails a gate. `Model routing: inherited` disables this. `Requested reasoning effort` is Cairn's desired profile; `Effective reasoning effort` is what the dispatch response confirms. Every new task/worker dispatch MUST pass the task's requested effort through the host option together with the chosen model; recording without passing is a defect. Effective is the requested value when the dispatch passed it and the host did not reject it, `not-applied: <reason>` when it was not passed, and `inherited` only when the host exposes no effort option. Infer capability only from the exposed interface or dispatch response, distinguish Cairn non-integration from host non-support, and change no global/user configuration.
21
+ - Choose the model per task (fast `haiku`, balanced `sonnet`, stronger `opus`: planning, triage, Heavy implementation, review, final verification/review; strongest `fable`: Heavy design only, as a separate `kind: "design"` task dispatched to that tier rather than part of planning) and record `Requested model`/`Effective model`; Effective is the passed value plus the exact model ID the worker reports from its own system prompt (`<value> (unreported)` when absent, never guess), `not-applied: <reason>`, or `inherited` when the host has no model option. Re-dispatch one tier up (fast, balanced, stronger, strongest) if a lower-tier worker fails a gate. A subagent's model and effort are fixed at dispatch; any re-dispatch that changes either (one tier up, Light → Heavy promotion, route-change recalculation) MUST be a new dispatch (Claude Code: a new Agent call), never `SendMessage` to the existing subagent. A design model that is not the host's strongest tier (`--host claude-code` `fable`, `codex` `gpt-6-astra`, `antigravity` `pro`; any of the three when `--host` is omitted) fails completion unless `--allow-no-design-model <reason>`. `Model routing: inherited` disables this. `Requested reasoning effort` is Cairn's desired profile; `Effective reasoning effort` is what the dispatch response confirms. Every new task/worker dispatch MUST pass the task's requested effort through the host option together with the chosen model; recording without passing is a defect. Effective is the requested value when the dispatch passed it and the host did not reject it, `not-applied: <reason>` when it was not passed, and `inherited` only when the host exposes no effort option. Infer capability only from the exposed interface or dispatch response, distinguish Cairn non-integration from host non-support, and change no global/user configuration.
22
22
  - In Claude Code, pass model with the Agent tool `model` parameter (`haiku|sonnet|opus|fable`) and effort with `effort` (`low|medium|high|xhigh|max`, same as Cairn values), for example `{"description": "Implement task-1", "subagent_type": "general-purpose", "model": "sonnet", "effort": "medium", "prompt": "..."}`. `subagent_type: "fork"` ignores `model` and `effort`; never dispatch routed work as a fork.
23
23
  - On Heavy Path, check plan gaps before implementation.
24
24
  - Review all three complexity records: the provisional request checkpoint, post-exploration planning checkpoint, and code checkpoint after exact file/caller/test inspection immediately before the first edit. Before editing, evidence may change either route. Every change must synchronize the plan artifact, repository goal task roadmap through `goal replan`, and native UI plan, including reviews and required evidence. After editing begins, a new Heavy signal promotes Light Path to Heavy Path: stop further edits, mark affected evidence stale, synchronize all three roadmaps, and repeat the code checkpoint.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cairn-ai",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Token-efficient agent harness with tool readiness for Codex, Claude Code, and Antigravity",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -54,6 +54,7 @@ export async function runGoalCli(args, {
54
54
  status: options.status ?? taskAction ?? positional[1],
55
55
  blocker: options.reason,
56
56
  effortWaiver: options["allow-inherited-effort"],
57
+ designWaiver: options["allow-no-design-model"],
57
58
  });
58
59
  emit(JSON.stringify(state));
59
60
  return state;
@@ -77,11 +78,21 @@ export async function runGoalCli(args, {
77
78
  effortSource: options["effort-source"],
78
79
  route: options.route,
79
80
  effortWaiver: options["allow-inherited-effort"],
81
+ kind: options.kind,
82
+ model: options.model,
83
+ modelSource: options["model-source"],
84
+ designWaiver: options["allow-no-design-model"],
85
+ effectiveModel: options["effective-model"],
86
+ modelHost: options.host,
80
87
  });
81
88
  const task = state.tasks.find((item) => item.id === String(options.task ?? positional[0]).trim());
82
89
  if (task?.route === "heavy" && task.assignedAgentId && (!task.effort || task.effortSource === "not-applied")) {
83
90
  stderr(`Cairn warning: Heavy task ${task.id} is assigned without an applied effort (effort ${task.effort ?? "none"}, source ${task.effortSource ?? "none"}); dispatch with the plan's effort and record --effort <level> --effort-source host-option.`);
84
91
  }
92
+ // A worker-reported ID that does not contain the passed alias hints at a silent fallback; warn, never block.
93
+ if (task?.model && task.effectiveModel && task.effectiveModel !== "unreported" && !task.effectiveModel.toLowerCase().includes(task.model.toLowerCase())) {
94
+ stderr(`Cairn warning: Task ${task.id} effective model ${task.effectiveModel} does not match the passed model ${task.model}; check the dispatch result.`);
95
+ }
85
96
  emit(JSON.stringify(state));
86
97
  return state;
87
98
  }
@@ -214,10 +225,10 @@ function allowedOptions(command) {
214
225
  const byCommand = {
215
226
  start: ["goal", "plan", "tasks", "criteria", "requiredEvidence", "session", "evidencePolicy", "evidence-policy"],
216
227
  status: [],
217
- task: ["task", "status", "reason", "allow-inherited-effort"],
228
+ task: ["task", "status", "reason", "allow-inherited-effort", "allow-no-design-model"],
218
229
  replan: ["tasks"],
219
230
  claim: ["session"],
220
- assign: ["task", "agent", "effort", "effort-source", "route", "allow-inherited-effort"],
231
+ assign: ["task", "agent", "effort", "effort-source", "route", "allow-inherited-effort", "kind", "model", "model-source", "allow-no-design-model", "effective-model", "host"],
221
232
  receipt: ["task", "kind", "scope", "command", "exitCode", "exit-code", "exit", "timestamp", "goalId", "goal-id", "plan"],
222
233
  verify: ["task", "kind", "scope", "watch", "timeout-ms"],
223
234
  finish: ["watch", "timeout-ms"],
@@ -182,6 +182,12 @@ export function goalStats(state, root = process.cwd()) {
182
182
  effort: task.effort ?? null,
183
183
  effortSource: task.effortSource ?? null,
184
184
  ...(task.effortWaiver ? { effortWaiver: task.effortWaiver } : {}),
185
+ ...(task.kind ? { kind: task.kind } : {}),
186
+ ...(task.model ? { model: task.model } : {}),
187
+ ...(task.modelSource ? { modelSource: task.modelSource } : {}),
188
+ ...(task.designWaiver ? { designWaiver: task.designWaiver } : {}),
189
+ ...(task.modelHost ? { modelHost: task.modelHost } : {}),
190
+ ...(task.effectiveModel ? { effectiveModel: task.effectiveModel } : {}),
185
191
  })),
186
192
  };
187
193
  }
@@ -4,10 +4,10 @@ import { cairnHome, worktreeId } from "./cairn-paths.mjs";
4
4
  import { assertNoSymlinkComponents } from "./cairn-safe-fs.mjs";
5
5
  import { goalStatePath, readGoalState, readGoalStateUnlocked, withGoalStateLock, writeGoalStateUnlocked } from "./cairn-goal-state.mjs";
6
6
  import {
7
- GOAL_STATE_VERSION, PLAN_ID_MAX_LENGTH, assertRecoveryReferenceBudget, boundedRequiredText,
8
- assertCanBeActive, assertEffortRecord, assertTaskGraph,
7
+ GOAL_STATE_VERSION, PLAN_ID_MAX_LENGTH, STRONGEST_MODEL_BY_HOST, assertRecoveryReferenceBudget, boundedRequiredText,
8
+ assertCanBeActive, assertEffortRecord, assertModelRecord, assertTaskGraph,
9
9
  defaultGoalEvidence, normalizeCriteria, normalizeTask, optionalText, requiredText,
10
- terminalGoalStatuses, timestamp, validEvidencePolicy, validGoalStatus, validTaskRoute, validTaskStatus, validateState,
10
+ terminalGoalStatuses, timestamp, validEvidencePolicy, validGoalStatus, validModelHost, validTaskKind, validTaskRoute, validTaskStatus, validateState,
11
11
  } from "./cairn-goal-state-validation.mjs";
12
12
  import {
13
13
  advanceGoal, assertGoalMutable, ensureGoalCanComplete, ensureTaskCanComplete,
@@ -119,11 +119,13 @@ function applyGoalTransition(state, nextStatus, blocker, root) {
119
119
  return state;
120
120
  }
121
121
 
122
- export async function setTaskStatus({ root = process.cwd(), taskId, status, blocker, effortWaiver } = {}) {
122
+ export async function setTaskStatus({ root = process.cwd(), taskId, status, blocker, effortWaiver, designWaiver } = {}) {
123
123
  const nextStatus = validTaskStatus(status);
124
124
  const normalizedTaskId = requiredText(taskId, "taskId");
125
125
  const normalizedWaiver = optionalText(effortWaiver, "effortWaiver");
126
126
  if (normalizedWaiver !== null && nextStatus !== "completed") throw new Error("--allow-inherited-effort applies only when completing a task");
127
+ const normalizedDesignWaiver = optionalText(designWaiver, "designWaiver");
128
+ if (normalizedDesignWaiver !== null && nextStatus !== "completed") throw new Error("--allow-no-design-model applies only when completing a task");
127
129
  return mutateGoal(root, (state) => {
128
130
  assertGoalMutable(state);
129
131
  const task = taskById(state, normalizedTaskId);
@@ -134,7 +136,9 @@ export async function setTaskStatus({ root = process.cwd(), taskId, status, bloc
134
136
  }
135
137
  if (nextStatus === "completed") {
136
138
  if (normalizedWaiver !== null) task.effortWaiver = normalizedWaiver;
139
+ if (normalizedDesignWaiver !== null) task.designWaiver = normalizedDesignWaiver;
137
140
  ensureHeavyEffortApplied(task);
141
+ ensureHeavyDesignRecorded(state, task);
138
142
  ensureTaskCanComplete(state, task, root);
139
143
  }
140
144
  if (nextStatus === "blocked") task.blocker = requiredText(blocker, "blocker");
@@ -201,10 +205,29 @@ function ensureHeavyEffortApplied(task) {
201
205
  throw new Error(`Heavy task ${task.id} assigned to ${task.assignedAgentId} has no applied effort (effort ${task.effort ?? "none"}, source ${source ?? "none"}); record goal assign ${task.id} --effort <low|medium|high|xhigh|max> --effort-source host-option, or complete with --allow-inherited-effort <reason>`);
202
206
  }
203
207
 
208
+ // A kind "design" task must record the strongest-tier model it was dispatched with (host-option). Any other assigned
209
+ // Heavy task needs a completed design task in the goal. A designWaiver reason bypasses both; Light and unassigned
210
+ // tasks are not checked.
211
+ function ensureHeavyDesignRecorded(state, task) {
212
+ if (task.designWaiver) return;
213
+ if (task.kind === "design") {
214
+ if (!(task.model && task.modelSource === "host-option")) {
215
+ throw new Error(`Design task ${task.id} has no recorded design model (model ${task.model ?? "none"}, source ${task.modelSource ?? "none"}); dispatch it to the strongest tier and record goal assign ${task.id} --model <value> --model-source host-option, or complete with --allow-no-design-model <reason>`);
216
+ }
217
+ // The host is declared, never guessed; without it any supported host's strongest value passes.
218
+ const expected = task.modelHost ? [STRONGEST_MODEL_BY_HOST[task.modelHost]] : Object.values(STRONGEST_MODEL_BY_HOST);
219
+ if (expected.includes(task.model)) return;
220
+ throw new Error(`Design task ${task.id} recorded model ${task.model}, expected ${expected.join(" | ")}${task.modelHost ? ` for host ${task.modelHost}` : ""}; dispatch it to the strongest tier and record goal assign ${task.id} --model <value> --model-source host-option, or complete with --allow-no-design-model <reason>`);
221
+ }
222
+ if (task.route !== "heavy" || !task.assignedAgentId) return;
223
+ if (state.tasks.some((item) => item.kind === "design" && item.status === "completed")) return;
224
+ throw new Error(`Heavy task ${task.id} assigned to ${task.assignedAgentId} has no completed design task; add a kind "design" task with goal replan, complete it with goal assign <design-task> --model <value> --model-source host-option, or complete with --allow-no-design-model <reason>`);
225
+ }
226
+
204
227
  // Omitted effort/route/waiver options keep stored values, so a reassignment can change only the agent. When effort
205
228
  // or effortSource is given, both are replaced together; an omitted source is inferred from the effort.
206
229
  export async function assignTask({
207
- root = process.cwd(), taskId, agentId = null, effort, effortSource, route, effortWaiver,
230
+ root = process.cwd(), taskId, agentId = null, effort, effortSource, route, effortWaiver, kind, model, modelSource, designWaiver, effectiveModel, modelHost,
208
231
  } = {}) {
209
232
  const normalizedTaskId = requiredText(taskId, "taskId");
210
233
  const normalizedAgentId = agentId === null ? null : requiredText(agentId, "agentId");
@@ -215,6 +238,14 @@ export async function assignTask({
215
238
  if (normalizedEffort === null && normalizedSource !== "not-applied") throw new Error("--effort-source requires --effort unless it is not-applied");
216
239
  assertEffortRecord(normalizedEffort, normalizedSource, "assign");
217
240
  }
241
+ const modelGiven = model !== undefined || modelSource !== undefined;
242
+ const normalizedModel = model === undefined || model === null ? null : requiredText(model, "model");
243
+ const normalizedModelSource = modelSource ?? (normalizedModel === null ? null : normalizedModel === "inherited" ? "inherited" : "host-option");
244
+ if (modelGiven) assertModelRecord(normalizedModel, normalizedModelSource, "assign");
245
+ const normalizedKind = kind === undefined ? undefined : validTaskKind(kind);
246
+ const normalizedDesignWaiver = designWaiver === undefined ? undefined : requiredText(designWaiver, "designWaiver");
247
+ const normalizedEffectiveModel = effectiveModel === undefined ? undefined : requiredText(effectiveModel, "effectiveModel");
248
+ const normalizedModelHost = modelHost === undefined ? undefined : validModelHost(modelHost);
218
249
  const normalizedRoute = route === undefined ? undefined : validTaskRoute(route);
219
250
  const normalizedWaiver = effortWaiver === undefined ? undefined : requiredText(effortWaiver, "effortWaiver");
220
251
  return mutateGoal(root, (state) => {
@@ -225,6 +256,14 @@ export async function assignTask({
225
256
  task.effort = normalizedEffort;
226
257
  task.effortSource = normalizedSource;
227
258
  }
259
+ if (modelGiven) {
260
+ task.model = normalizedModel;
261
+ task.modelSource = normalizedModelSource;
262
+ }
263
+ if (normalizedKind !== undefined) task.kind = normalizedKind;
264
+ if (normalizedDesignWaiver !== undefined) task.designWaiver = normalizedDesignWaiver;
265
+ if (normalizedEffectiveModel !== undefined) task.effectiveModel = normalizedEffectiveModel;
266
+ if (normalizedModelHost !== undefined) task.modelHost = normalizedModelHost;
228
267
  if (normalizedRoute !== undefined) task.route = normalizedRoute;
229
268
  if (normalizedWaiver !== undefined) task.effortWaiver = normalizedWaiver;
230
269
  task.updatedAt = timestamp();
@@ -25,6 +25,9 @@ export const TASK_ROUTES = new Set(["light", "heavy"]);
25
25
  export const CONCRETE_EFFORTS = new Set(["low", "medium", "high", "xhigh", "max"]);
26
26
  export const TASK_EFFORTS = new Set([...CONCRETE_EFFORTS, "inherited"]);
27
27
  export const EFFORT_SOURCES = new Set(["host-option", "inherited", "not-applied"]);
28
+ export const TASK_KINDS = new Set(["design"]);
29
+ // The host's strongest tier is the value passed at dispatch; mirrors the strongest column in skills/cairn-plan/delegation.md.
30
+ export const STRONGEST_MODEL_BY_HOST = Object.freeze({ "claude-code": "fable", codex: "gpt-6-astra", antigravity: "pro" });
28
31
 
29
32
  export function validateState(value) {
30
33
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Cairn goal state must be an object");
@@ -66,6 +69,11 @@ export function validateState(value) {
66
69
  if (task.route !== null && task.route !== undefined) validTaskRoute(task.route, `task ${task.id} route`);
67
70
  assertEffortRecord(task.effort ?? null, task.effortSource ?? null, `task ${task.id}`);
68
71
  if (task.effortWaiver !== null && task.effortWaiver !== undefined) requiredText(task.effortWaiver, `task ${task.id} effortWaiver`);
72
+ if (task.kind !== null && task.kind !== undefined) validTaskKind(task.kind, `task ${task.id} kind`);
73
+ assertModelRecord(task.model ?? null, task.modelSource ?? null, `task ${task.id}`);
74
+ if (task.designWaiver !== null && task.designWaiver !== undefined) requiredText(task.designWaiver, `task ${task.id} designWaiver`);
75
+ if (task.modelHost !== null && task.modelHost !== undefined) validModelHost(task.modelHost, `task ${task.id} modelHost`);
76
+ if (task.effectiveModel !== null && task.effectiveModel !== undefined) requiredText(task.effectiveModel, `task ${task.id} effectiveModel`);
69
77
  validTimestamp(task.createdAt);
70
78
  validTimestamp(task.updatedAt);
71
79
  }
@@ -120,6 +128,12 @@ export function normalizeTask(task, index, defaultStatus, previousTaskId = null)
120
128
  effort: null,
121
129
  effortSource: null,
122
130
  effortWaiver: null,
131
+ kind: source.kind === undefined || source.kind === null ? null : validTaskKind(source.kind, `tasks[${index}].kind`),
132
+ model: null,
133
+ modelSource: null,
134
+ designWaiver: null,
135
+ modelHost: null,
136
+ effectiveModel: null,
123
137
  blocker: null,
124
138
  createdAt: timestamp(),
125
139
  updatedAt: timestamp(),
@@ -131,6 +145,25 @@ export function validTaskRoute(value, label = "route") {
131
145
  return value;
132
146
  }
133
147
 
148
+ export function validModelHost(value, label = "modelHost") {
149
+ if (!Object.hasOwn(STRONGEST_MODEL_BY_HOST, value)) throw new Error(`${label} must be one of: ${Object.keys(STRONGEST_MODEL_BY_HOST).join(", ")}`);
150
+ return value;
151
+ }
152
+
153
+ export function validTaskKind(value, label = "kind") {
154
+ if (!TASK_KINDS.has(value)) throw new Error(`${label} must be one of: ${[...TASK_KINDS].join(", ")}`);
155
+ return value;
156
+ }
157
+
158
+ // A model is a non-empty host-specific string; source "host-option" needs a model, and a model needs a source.
159
+ export function assertModelRecord(model, source, label = "task") {
160
+ if (model !== null) requiredText(model, `${label} model`);
161
+ if (source !== null && !EFFORT_SOURCES.has(source)) throw new Error(`${label} modelSource must be one of: ${[...EFFORT_SOURCES].join(", ")}`);
162
+ if (source === "host-option" && model === null) throw new Error(`${label} modelSource host-option requires a model`);
163
+ if (source === "inherited" && model === null) throw new Error(`${label} modelSource inherited requires a model`);
164
+ if (model !== null && source === null) throw new Error(`${label} model requires a modelSource`);
165
+ }
166
+
134
167
  // effort "inherited" pairs only with source "inherited"; source "host-option" needs a concrete level; a source
135
168
  // without an effort is only meaningful as "not-applied".
136
169
  export function assertEffortRecord(effort, source, label = "task") {
@@ -42,7 +42,7 @@ Record three triage checkpoints: request (provisional), planning (after explorat
42
42
  1. Run `node "<pluginRoot>/scripts/cairn.mjs" init --root "<repoRoot>"`. If unavailable, create `MEMORY.md`, `PLAN.md`, `docs/memory`, `docs/plan` and report that `cairn doctor`/`cairn upgrade` is needed.
43
43
  2. Read `MEMORY.md` when present. A non-trivial implementation request authorizes the plan and goal even if the user never says "goal".
44
44
  3. Write the initial plan `docs/plan/<topic>.md` from `cairn://templates/work-plan.md` plus its `PLAN.md` entry from the request and `MEMORY.md`: outcome, anticipated surfaces, stable roadmap, and `triage-plan` as first active task (investigation scope, toolcheck, Heavy criteria, update condition); mark it `initial: decision-complete for triage; not implementation-ready`.
45
- 4. Before exploration, toolcheck, delegation, or a non-blocking question, call Codex `update_plan` first and then `create_goal` when available (record unavailability; never claim sync that did not occur), then start the repository goal:
45
+ 4. Before exploration, toolcheck, delegation, or a non-blocking question, call Codex `update_plan` first and then `create_goal` when available (main agent only, subagents skip; record unavailability; never claim sync that did not occur), then start the repository goal:
46
46
 
47
47
  ```sh
48
48
  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>"
@@ -13,6 +13,7 @@ Reference for `cairn-plan`. Read before dispatching `explorer` or `worker` agent
13
13
  - If subagent tools are unavailable, the main agent takes over implementation directly and records that takeover in evidence; do not stop solely because subagent tools are unavailable.
14
14
  - Keep urgent non-implementation blocking work local when the next step depends immediately on the result.
15
15
  - Tell every delegated agent and child subagent to read project-root `MEMORY.md` when present and continue when absent, keep scope, and tell workers they are not alone in the codebase and must not revert others' edits.
16
+ - Host task-list tools (`update_plan`, `create_goal`, `wrap_up`) are called by the main agent only; a worker or subagent skips them (hosts reject them from subagents) and reports in its final report.
16
17
  - Every plan must require these status, final-report, close-and-review, and recursive-delegation rules.
17
18
 
18
19
  ## Task Graph
@@ -30,7 +31,7 @@ EXPECTED OUTCOME: Return concrete decisions, file paths, dependencies, risks, an
30
31
  REQUIRED TOOLS: Read-only repository exploration, tool readiness checks, LSP/symbol tools, local command checks.
31
32
  MODEL: <tier/value> (passed via host option)
32
33
  REASONING EFFORT: <value> (passed via host option)
33
- MUST DO: Read the project-root `MEMORY.md` before doing assigned work. Preserve proper nouns exactly. Prefer repository evidence. Report missing tools and obtain explicit user approval before any installation. Identify assumptions. Follow the subagent lifecycle rules (status reporting when a channel exists, final report before leaving, recursive delegation only for bounded sub-tasks, passing each child's reasoning effort through the host option). Tell workers they are not alone in the codebase and must not revert others' edits. Use the OS locale for user-visible responses and generated or updated documentation, plans, and memory artifacts.
34
+ MUST DO: Read the project-root `MEMORY.md` before doing assigned work. Preserve proper nouns exactly. Prefer repository evidence. Report missing tools and obtain explicit user approval before any installation. Identify assumptions. Follow the subagent lifecycle rules (status reporting when a channel exists, final report before leaving, recursive delegation only for bounded sub-tasks, passing each child's reasoning effort through the host option; report the exact model ID from your own system prompt in the final report, `unreported` if absent, never guess; skip host task-list tools `update_plan`, `create_goal`, `wrap_up`). Tell workers they are not alone in the codebase and must not revert others' edits. Use the OS locale for user-visible responses and generated or updated documentation, plans, and memory artifacts.
34
35
  MUST NOT DO: Edit production code. Ask the user. Expand scope. Delegate vague or unbounded work.
35
36
  CONTEXT: User request, MEMORY.md, relevant docs/memory notes, applied model guidance, repository root, Cairn CLI prefix line from the session-start hook.
36
37
  ```
@@ -40,8 +41,9 @@ CONTEXT: User request, MEMORY.md, relevant docs/memory notes, applied model guid
40
41
  This skill explicitly instructs the effort level for delegated work. It also instructs each task's model.
41
42
 
42
43
  - The plan decides each task's model from its nature and records `Requested model` and `Effective model`; models are not always inherited. Tiers: fast = read-only exploration, search, inventory, summarization (Claude Code `haiku`); balanced = Light Path implementation and routine verification (`sonnet`); stronger = planning, complexity triage, Heavy Path implementation, review, and final verification/review (`opus`); strongest = Heavy Path design only (`fable`).
43
- - Model Effective: passed and not rejected is that value; not passed is `not-applied: <reason>`; `inherited` only when the host has no model option. Codex passes the tier's slug (table below) through the host-native model option, else `inherited`.
44
+ - Model Effective is the passed value plus the exact model ID the worker reports from its own system prompt, e.g. `fable (claude-fable-5-1)`; when the report has no ID, record `<value> (unreported)` and never guess, via `goal assign --effective-model <id|unreported>`. Not passed is `not-applied: <reason>`; `inherited` only when the host has no model option. Codex passes the tier's slug (table below) through the host-native model option, else `inherited`.
44
45
  - If a lower-tier worker fails a gate, re-dispatch that task one tier up (fast, balanced, stronger, strongest) before recording a blocker.
46
+ - A subagent's model and effort are fixed at dispatch. Any re-dispatch that changes either (one tier up, Light → Heavy promotion, route-change recalculation) MUST be a new dispatch (Claude Code: a new Agent call); never continue the existing subagent with `SendMessage`, which keeps its original model and effort.
45
47
  - Routing is per dispatch only; never change model or global/user config files. `Model routing: inherited` in `MEMORY.md` or the plan disables per-task selection.
46
48
  - Light Path planning, implementation, and verification request `medium`.
47
49
  - Heavy Path planning, review, and implementation request `high`; final verification and review request `xhigh`.
@@ -58,5 +60,5 @@ This skill explicitly instructs the effort level for delegated work. It also ins
58
60
  | Antigravity | `invoke_subagent` `Model` takes only a tier enum: fast `flash_lite`, balanced `flash`, stronger `pro`, strongest `pro` (no tier above `pro`); `inherit` is not used for routed work. No effort option. | model: passed value; effort: `inherited` |
59
61
  | Host without an option | None. | `inherited` |
60
62
 
61
- - After each dispatch, record state with `goal assign --task <id> --agent <id> --effort <value> --effort-source <host-option|inherited|not-applied>`. Mark Heavy tasks with `route: "heavy"` (tasks JSON field or `goal assign --route heavy`). A Heavy task with no effort, `not-applied`, or `inherited` fails `goal task --status completed` unless `--allow-inherited-effort <reason>` records why.
63
+ - After each dispatch, record state with `goal assign --task <id> --agent <id> --effort <value> --effort-source <host-option|inherited|not-applied>`; for a `kind: "design"` task also `--model <value> --model-source host-option --host <claude-code|codex|antigravity>`. A design model that is not the host's strongest tier (`--host claude-code` `fable`, `codex` `gpt-6-astra`, `antigravity` `pro`; any of the three when `--host` is omitted) fails completion unless `--allow-no-design-model <reason>`. Mark Heavy tasks with `route: "heavy"` (tasks JSON field or `goal assign --route heavy`). A Heavy task with no effort, `not-applied`, or `inherited` fails `goal task --status completed` unless `--allow-inherited-effort <reason>` records why.
62
64
  - On every route change, synchronize the plan artifact, repository goal task roadmap through `goal replan`, native UI plan, and reasoning effort profile before mutation. Completed task profiles are preserved as audit history; incomplete task profiles are recalculated for the new path.
@@ -4,8 +4,9 @@ Reference for `cairn-plan`. Read only when a Heavy Path signal is present or a r
4
4
 
5
5
  ## Heavy Path Flow
6
6
 
7
- Heavy Path flow: plan -> pre-implementation review -> bounded `worker` implementation -> verification worker or read-only review.
7
+ Heavy Path flow: plan -> Heavy design task -> pre-implementation review -> bounded `worker` implementation -> verification worker or read-only review.
8
8
 
9
+ - On a Heavy route, `goal replan` adds a `kind: "design"` task (`heavy-design`, `route: "heavy"`, `requiredEvidence: ["designRecord"]`) after `triage-plan`, and implementation tasks depend on it. The main agent never does this design inline: dispatch it read-only to the strongest tier (Claude Code Agent `model: "fable"`, `effort: "high"`, never `fork`), record `goal assign heavy-design --model <value> --model-source host-option --host <claude-code|codex|antigravity>`, merge the result into the plan's `## Heavy Design`, and bind that plan with `goal verify --kind designRecord`. A design model that is not the host's strongest tier (`--host claude-code` `fable`, `codex` `gpt-6-astra`, `antigravity` `pro`; any of the three when `--host` is omitted) fails completion unless `--allow-no-design-model <reason>`. This is the only bypass.
9
10
  - Record the pre-implementation decisions that review must resolve before any mutation.
10
11
  - Every Heavy Path task includes an explicit automated test command and a `Tests:` evidence line; completion cannot rely on generic verification wording without test evidence.
11
12
  - Planning, review, and implementation request `high` reasoning effort; final verification and review request `xhigh` (see `cairn://skills/cairn-plan/delegation.md`).
@@ -23,4 +24,5 @@ Use three mandatory checkpoints and record each result, evidence, newly discover
23
24
  - Before the first edit, the planning or code checkpoint may change Light Path to Heavy Path or Heavy Path to Light Path when the evidence supports it.
24
25
  - Every route change rebuilds and synchronizes the plan artifact, the repository goal task roadmap through `goal replan`, and the native UI plan, including assignments, reviews, required evidence, and the reasoning effort profile. Do not mutate until all agree.
25
26
  - After editing starts, only promote Light Path to Heavy Path: stop further edits, mark affected evidence stale, revise and synchronize all roadmaps, run newly required Heavy Path review, and repeat the code checkpoint before resuming.
27
+ - A subagent's model and effort are fixed at dispatch; any re-dispatch that changes either (one tier up, Light → Heavy promotion, route-change recalculation) MUST be a new dispatch (Claude Code: a new Agent call), never `SendMessage` to the existing subagent.
26
28
  - A Quick Path change that meets any failed Quick Path criterion escalates to Light Path first (plan + goal) before further edits; a Heavy signal then follows this file.
@@ -11,7 +11,7 @@ Plan from the whole work down. First understand the full requested outcome and a
11
11
  ## Planning Rules
12
12
 
13
13
  - 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.
14
- - Every non-trivial implementation or planned-work continuation plan must be shown through Codex `update_plan` and `create_goal` before triage when those tools are available, then synchronized again after triage. Routine known-target Git/GitHub operations and Quick Path work do not create a plan or goal.
14
+ - Every non-trivial implementation or planned-work continuation plan must be shown through Codex `update_plan` and `create_goal` before triage when those tools are available (host task-list tools `update_plan`, `create_goal`, `wrap_up` are called by the main agent only; a worker or subagent skips them and reports in its final report), then synchronized again after triage. Routine known-target Git/GitHub operations and Quick Path work do not create a plan or goal.
15
15
  - Every plan must describe the whole work first, then classify it into executable tasks and any needed sub-tasks.
16
16
  - Every plan must treat the user-called/main agent as the orchestrator and assign implementation edits to `worker` whenever subagent tools are available, regardless of Light Path or Heavy Path (see `cairn://skills/cairn-plan/delegation.md`).
17
17
  - Every plan must include complexity triage, the selected path, and the Heavy Path signals checked, even when Light Path is selected.
@@ -9,6 +9,7 @@ Reference for `cairn-work`. Read before dispatching a `worker` or `explorer`, or
9
9
  - 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.
10
10
  - Delegated subagents must provide a final report before leaving. After the orchestrator captures the final report and evidence, it must close or release the completed subagent, then review the final report and evidence before marking the work complete.
11
11
  - When subagent tools are available, each agent may recursively delegate bounded sub-tasks to subagents. Every child subagent reads project-root `MEMORY.md` when present and continues when absent, keeps the assigned scope, and preserves others' edits.
12
+ - Host task-list tools (`update_plan`, `create_goal`, `wrap_up`) are called by the main agent only; a worker or subagent skips them (hosts reject them from subagents) and reports in its final report.
12
13
  - If subagent tools are unavailable, the main agent takes over implementation directly and records that takeover in evidence.
13
14
  - `worker` returns a structured handoff for only its assigned goal/task: 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.
14
15
 
@@ -30,7 +31,7 @@ EXPECTED OUTCOME: Minimal code or documentation change satisfying the task contr
30
31
  REQUIRED TOOLS: Scoped file editing, tool readiness checks, existing test/build commands, surface QA commands.
31
32
  MODEL: <tier/value> (passed via host option)
32
33
  REASONING EFFORT: <value> (passed via host option)
33
- MUST DO: Read the project-root `MEMORY.md` before doing assigned work and follow its policy. Stay within the listed file scope and the task's `writeScope`. Assume you are not alone in the codebase; do not revert others' edits. Follow the subagent lifecycle rules (status reporting when a channel exists, final report before leaving, recursive delegation only for bounded sub-tasks with these same constraints, passing each child's reasoning effort through the host option). Obtain explicit user approval before installing missing tools. Run dry-run or check mode before external-state mutation when available. Provide module evidence, surface evidence, and a structured handoff scoped to the assigned goal/task. Never edit, weaken, skip, or delete existing tests, fixtures, or verification commands to pass a gate without a reason recorded in the plan. After a defect fix, search for other implementations of the same rule or pattern and report them. Copy the Cairn CLI prefix line literally; never store it in a shell variable. Use the OS locale for user-visible responses and generated or updated documentation, plans, and memory artifacts.
34
+ MUST DO: Read the project-root `MEMORY.md` before doing assigned work and follow its policy. Stay within the listed file scope and the task's `writeScope`. Assume you are not alone in the codebase; do not revert others' edits. Follow the subagent lifecycle rules (status reporting when a channel exists, final report before leaving, recursive delegation only for bounded sub-tasks with these same constraints, passing each child's reasoning effort through the host option; report the exact model ID from your own system prompt in the final report, `unreported` if absent, never guess; skip host task-list tools `update_plan`, `create_goal`, `wrap_up`). Obtain explicit user approval before installing missing tools. Run dry-run or check mode before external-state mutation when available. Provide module evidence, surface evidence, and a structured handoff scoped to the assigned goal/task. Never edit, weaken, skip, or delete existing tests, fixtures, or verification commands to pass a gate without a reason recorded in the plan. After a defect fix, search for other implementations of the same rule or pattern and report them. Copy the Cairn CLI prefix line literally; never store it in a shell variable. Use the OS locale for user-visible responses and generated or updated documentation, plans, and memory artifacts.
34
35
  MUST NOT DO: Ask the user. Skip LSP/typecheck/lint because a tool is missing without attempting installation. Refactor unrelated code. Continue an open-ended verification loop. Treat a side question as permission to abandon the active task. Delegate vague or unbounded work. Claim completion without evidence. Modify files outside the assigned ownership.
35
36
  CONTEXT: docs/plan/<topic>.md task, relevant docs/memory notes, applied model guidance, exact files, verification commands, Cairn CLI prefix line.
36
37
  ```
@@ -40,8 +41,9 @@ CONTEXT: docs/plan/<topic>.md task, relevant docs/memory notes, applied model gu
40
41
  This skill explicitly instructs the effort level for delegated work. It also instructs each task's model.
41
42
 
42
43
  - The plan decides each task's model from its nature and records `Requested model` and `Effective model`; models are not always inherited. Tiers: fast = read-only exploration, search, inventory, summarization (Claude Code `haiku`); balanced = Light Path implementation and routine verification (`sonnet`); stronger = planning, complexity triage, Heavy Path implementation, review, and final verification/review (`opus`); strongest = Heavy Path design only (`fable`).
43
- - Model Effective: passed and not rejected is that value; not passed is `not-applied: <reason>`; `inherited` only when the host has no model option. Codex passes the tier's slug (table below) through the host-native model option, else `inherited`.
44
+ - Model Effective is the passed value plus the exact model ID the worker reports from its own system prompt, e.g. `fable (claude-fable-5-1)`; when the report has no ID, record `<value> (unreported)` and never guess, via `goal assign --effective-model <id|unreported>`. Not passed is `not-applied: <reason>`; `inherited` only when the host has no model option. Codex passes the tier's slug (table below) through the host-native model option, else `inherited`.
44
45
  - If a lower-tier worker fails a gate, re-dispatch that task one tier up (fast, balanced, stronger, strongest) before recording a blocker.
46
+ - A subagent's model and effort are fixed at dispatch. Any re-dispatch that changes either (one tier up, Light → Heavy promotion, route-change recalculation) MUST be a new dispatch (Claude Code: a new Agent call); never continue the existing subagent with `SendMessage`, which keeps its original model and effort.
45
47
  - Routing is per dispatch only; never change model or global/user config files. `Model routing: inherited` in `MEMORY.md` or the plan disables per-task selection.
46
48
  - Light Path planning, implementation, and verification request `medium`.
47
49
  - Heavy Path planning, review, and implementation request `high`; final verification and review request `xhigh`.
@@ -58,5 +60,5 @@ This skill explicitly instructs the effort level for delegated work. It also ins
58
60
  | Antigravity | `invoke_subagent` `Model` takes only a tier enum: fast `flash_lite`, balanced `flash`, stronger `pro`, strongest `pro` (no tier above `pro`); `inherit` is not used for routed work. No effort option. | model: passed value; effort: `inherited` |
59
61
  | Host without an option | None. | `inherited` |
60
62
 
61
- - After each dispatch, record state with `goal assign --task <id> --agent <id> --effort <value> --effort-source <host-option|inherited|not-applied>`. Mark Heavy tasks with `route: "heavy"` (tasks JSON field or `goal assign --route heavy`). A Heavy task with no effort, `not-applied`, or `inherited` fails `goal task --status completed` unless `--allow-inherited-effort <reason>` records why.
63
+ - After each dispatch, record state with `goal assign --task <id> --agent <id> --effort <value> --effort-source <host-option|inherited|not-applied>`; for a `kind: "design"` task also `--model <value> --model-source host-option --host <claude-code|codex|antigravity>`. A design model that is not the host's strongest tier (`--host claude-code` `fable`, `codex` `gpt-6-astra`, `antigravity` `pro`; any of the three when `--host` is omitted) fails completion unless `--allow-no-design-model <reason>`. Mark Heavy tasks with `route: "heavy"` (tasks JSON field or `goal assign --route heavy`). A Heavy task with no effort, `not-applied`, or `inherited` fails `goal task --status completed` unless `--allow-inherited-effort <reason>` records why.
62
64
  - On every route change, synchronize the plan artifact, repository goal task roadmap through `goal replan`, native UI plan, and reasoning effort profile before edits resume. Completed task profiles are preserved as audit history; incomplete task profiles are recalculated for the new path.
@@ -5,6 +5,7 @@ Reference for `cairn-work`. Read only when the recorded route is Heavy Path, a n
5
5
  ## Heavy Path Execution
6
6
 
7
7
  - Follow the full planning and review pipeline recorded in the plan without role-mapping shortcuts: pre-implementation review before mutation, bounded `worker` implementation, then verification worker or read-only review after evidence exists.
8
+ - Heavy implementation cannot complete until a completed `kind: "design"` task exists (record it with `--host <claude-code|codex|antigravity>`). A design model that is not the host's strongest tier (`--host claude-code` `fable`, `codex` `gpt-6-astra`, `antigravity` `pro`; any of the three when `--host` is omitted) fails completion unless `--allow-no-design-model <reason>`. This is the only bypass.
8
9
  - Run the recorded automated test command and record explicit `Tests:` evidence in the plan before claiming completion. This is required even when module acceptance uses a broader check command.
9
10
  - Planning, review, and implementation request `high` reasoning effort; final verification and review request `xhigh`.
10
11
 
@@ -13,4 +14,5 @@ Reference for `cairn-work`. Read only when the recorded route is Heavy Path, a n
13
14
  1. Confirm the request checkpoint and planning checkpoint, inspect the exact files, callers, and tests, then record the code checkpoint immediately before the first edit. Missing or pending checkpoints block mutation.
14
15
  2. Before the first edit, evidence may change Light Path to Heavy Path or Heavy Path to Light Path. On every route change, update the plan artifact first, replace the incomplete repository goal task roadmap with `goal replan`, update the native UI plan to the same stable task order, and synchronize assignments, reviews, required evidence, and the reasoning effort profile. Do not mutate until all agree.
15
16
  3. After editing starts, only promote Light Path to Heavy Path. On a new Heavy Path signal, stop further edits, mark affected evidence stale, revise and synchronize the plan artifact, repository goal task roadmap, and native UI plan, run newly required Heavy Path review, and repeat the code checkpoint before resuming.
16
- 4. Quick Path work that hits any failed Quick Path criterion first escalates to Light Path (create plan + goal) before further edits; a Heavy signal then follows step 3.
17
+ 4. A subagent's model and effort are fixed at dispatch; any re-dispatch that changes either (one tier up, Light → Heavy promotion, route-change recalculation) MUST be a new dispatch (Claude Code: a new Agent call), never `SendMessage` to the existing subagent.
18
+ 5. Quick Path work that hits any failed Quick Path criterion first escalates to Light Path (create plan + goal) before further edits; a Heavy signal then follows step 3.
@@ -41,14 +41,14 @@ Describe the observable result.
41
41
 
42
42
  ## Reasoning Effort Profile
43
43
 
44
- - Model: decided per task (fast `haiku` for read-only exploration/search/summarization; balanced `sonnet` for Light implementation and routine verification; stronger `opus` for planning, complexity triage, Heavy implementation, review, and final verification/review; strongest `fable` for Heavy design only). Pass it through the host option on every dispatch (Claude Code: Agent `model`, never `fork`); re-dispatch one tier up (fast, balanced, stronger, strongest) if a lower-tier worker fails a gate; `Model routing: inherited` disables this. Never change model/global config.
44
+ - Model: decided per task (fast `haiku` for read-only exploration/search/summarization; balanced `sonnet` for Light implementation and routine verification; stronger `opus` for planning, complexity triage, Heavy implementation, review, and final verification/review; strongest `fable` for Heavy design only, as a separate `kind: "design"` task dispatched to that tier rather than part of planning). Pass it through the host option on every dispatch (Claude Code: Agent `model`, never `fork`); re-dispatch one tier up (fast, balanced, stronger, strongest) if a lower-tier worker fails a gate; `Model routing: inherited` disables this. Effective model adds the exact model ID the worker reports from its own system prompt (`unreported` when absent; never guess). A re-dispatch that changes model or effort MUST be a new dispatch, never `SendMessage`. A design model below the host's strongest tier (`--host`: `fable`/`gpt-6-astra`/`pro`) fails completion unless `--allow-no-design-model <reason>`. Never change model/global config.
45
45
  - Light Path: planning=`medium`, implementation=`medium`, verification=`medium`.
46
46
  - Heavy Path: planning/review=`high`, implementation=`high`, final verification/review=`xhigh`.
47
47
  - Host boundary: every new task/worker dispatch MUST pass the task's requested effort through the host option (Claude Code: Agent `effort`, never `fork`; Codex: host-native option). Recording without passing is a defect. Use `inherited` only when the host has no option.
48
48
  - Route-change gate: synchronize the plan artifact, repository goal task roadmap through `goal replan`, native UI plan, and reasoning effort profile before edits resume. Completed task profiles are preserved as audit history; incomplete task profiles are recalculated for the new path.
49
49
  - Every module task records:
50
50
  - Requested model:
51
- - Effective model: <applied value | not-applied: reason | inherited: host has no option>
51
+ - Effective model: <applied value (reported model ID | unreported) | not-applied: reason | inherited: host has no option>
52
52
  - Requested reasoning effort:
53
53
  - Effective reasoning effort: <applied value | not-applied: reason | inherited: host has no option>
54
54
 
@@ -121,7 +121,7 @@ Describe the observable result.
121
121
  - Task ID: `triage-plan`
122
122
  - Initial status: `active`
123
123
  - Requested model:
124
- - Effective model: <applied value | not-applied: reason | inherited: host has no option>
124
+ - Effective model: <applied value (reported model ID | unreported) | not-applied: reason | inherited: host has no option>
125
125
  - Requested reasoning effort:
126
126
  - Effective reasoning effort: <applied value | not-applied: reason | inherited: host has no option>
127
127
  - Contract: execute the investigation scope and Heavy Path criteria declared by the initial plan, then update this file and the Codex UI plan to the decision-complete implementation revision.
@@ -134,7 +134,7 @@ Describe the observable result.
134
134
  - Task ID: `task-1`
135
135
  - Initial status: `pending` or `active`
136
136
  - Requested model:
137
- - Effective model: <applied value | not-applied: reason | inherited: host has no option>
137
+ - Effective model: <applied value (reported model ID | unreported) | not-applied: reason | inherited: host has no option>
138
138
  - Requested reasoning effort:
139
139
  - Effective reasoning effort: <applied value | not-applied: reason | inherited: host has no option>
140
140
  - Contract: