cairn-ai 0.4.0 → 0.4.1

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.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cairn",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Token-efficient agent harness with tool readiness for Codex, Claude Code, and Antigravity.",
5
5
  "author": {
6
6
  "name": "WkNam"
@@ -50,7 +50,7 @@ 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`.
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
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.
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`.
@@ -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, `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.
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.4.1",
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,6 +78,10 @@ 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"],
80
85
  });
81
86
  const task = state.tasks.find((item) => item.id === String(options.task ?? positional[0]).trim());
82
87
  if (task?.route === "heavy" && task.assignedAgentId && (!task.effort || task.effortSource === "not-applied")) {
@@ -214,10 +219,10 @@ function allowedOptions(command) {
214
219
  const byCommand = {
215
220
  start: ["goal", "plan", "tasks", "criteria", "requiredEvidence", "session", "evidencePolicy", "evidence-policy"],
216
221
  status: [],
217
- task: ["task", "status", "reason", "allow-inherited-effort"],
222
+ task: ["task", "status", "reason", "allow-inherited-effort", "allow-no-design-model"],
218
223
  replan: ["tasks"],
219
224
  claim: ["session"],
220
- assign: ["task", "agent", "effort", "effort-source", "route", "allow-inherited-effort"],
225
+ assign: ["task", "agent", "effort", "effort-source", "route", "allow-inherited-effort", "kind", "model", "model-source", "allow-no-design-model"],
221
226
  receipt: ["task", "kind", "scope", "command", "exitCode", "exit-code", "exit", "timestamp", "goalId", "goal-id", "plan"],
222
227
  verify: ["task", "kind", "scope", "watch", "timeout-ms"],
223
228
  finish: ["watch", "timeout-ms"],
@@ -182,6 +182,10 @@ 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 } : {}),
185
189
  })),
186
190
  };
187
191
  }
@@ -5,9 +5,9 @@ import { assertNoSymlinkComponents } from "./cairn-safe-fs.mjs";
5
5
  import { goalStatePath, readGoalState, readGoalStateUnlocked, withGoalStateLock, writeGoalStateUnlocked } from "./cairn-goal-state.mjs";
6
6
  import {
7
7
  GOAL_STATE_VERSION, PLAN_ID_MAX_LENGTH, assertRecoveryReferenceBudget, boundedRequiredText,
8
- assertCanBeActive, assertEffortRecord, assertTaskGraph,
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, 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,24 @@ 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") return;
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
+ if (task.route !== "heavy" || !task.assignedAgentId) return;
218
+ if (state.tasks.some((item) => item.kind === "design" && item.status === "completed")) return;
219
+ 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>`);
220
+ }
221
+
204
222
  // Omitted effort/route/waiver options keep stored values, so a reassignment can change only the agent. When effort
205
223
  // or effortSource is given, both are replaced together; an omitted source is inferred from the effort.
206
224
  export async function assignTask({
207
- root = process.cwd(), taskId, agentId = null, effort, effortSource, route, effortWaiver,
225
+ root = process.cwd(), taskId, agentId = null, effort, effortSource, route, effortWaiver, kind, model, modelSource, designWaiver,
208
226
  } = {}) {
209
227
  const normalizedTaskId = requiredText(taskId, "taskId");
210
228
  const normalizedAgentId = agentId === null ? null : requiredText(agentId, "agentId");
@@ -215,6 +233,12 @@ export async function assignTask({
215
233
  if (normalizedEffort === null && normalizedSource !== "not-applied") throw new Error("--effort-source requires --effort unless it is not-applied");
216
234
  assertEffortRecord(normalizedEffort, normalizedSource, "assign");
217
235
  }
236
+ const modelGiven = model !== undefined || modelSource !== undefined;
237
+ const normalizedModel = model === undefined || model === null ? null : requiredText(model, "model");
238
+ const normalizedModelSource = modelSource ?? (normalizedModel === null ? null : normalizedModel === "inherited" ? "inherited" : "host-option");
239
+ if (modelGiven) assertModelRecord(normalizedModel, normalizedModelSource, "assign");
240
+ const normalizedKind = kind === undefined ? undefined : validTaskKind(kind);
241
+ const normalizedDesignWaiver = designWaiver === undefined ? undefined : requiredText(designWaiver, "designWaiver");
218
242
  const normalizedRoute = route === undefined ? undefined : validTaskRoute(route);
219
243
  const normalizedWaiver = effortWaiver === undefined ? undefined : requiredText(effortWaiver, "effortWaiver");
220
244
  return mutateGoal(root, (state) => {
@@ -225,6 +249,12 @@ export async function assignTask({
225
249
  task.effort = normalizedEffort;
226
250
  task.effortSource = normalizedSource;
227
251
  }
252
+ if (modelGiven) {
253
+ task.model = normalizedModel;
254
+ task.modelSource = normalizedModelSource;
255
+ }
256
+ if (normalizedKind !== undefined) task.kind = normalizedKind;
257
+ if (normalizedDesignWaiver !== undefined) task.designWaiver = normalizedDesignWaiver;
228
258
  if (normalizedRoute !== undefined) task.route = normalizedRoute;
229
259
  if (normalizedWaiver !== undefined) task.effortWaiver = normalizedWaiver;
230
260
  task.updatedAt = timestamp();
@@ -25,6 +25,7 @@ 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"]);
28
29
 
29
30
  export function validateState(value) {
30
31
  if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("Cairn goal state must be an object");
@@ -66,6 +67,9 @@ export function validateState(value) {
66
67
  if (task.route !== null && task.route !== undefined) validTaskRoute(task.route, `task ${task.id} route`);
67
68
  assertEffortRecord(task.effort ?? null, task.effortSource ?? null, `task ${task.id}`);
68
69
  if (task.effortWaiver !== null && task.effortWaiver !== undefined) requiredText(task.effortWaiver, `task ${task.id} effortWaiver`);
70
+ if (task.kind !== null && task.kind !== undefined) validTaskKind(task.kind, `task ${task.id} kind`);
71
+ assertModelRecord(task.model ?? null, task.modelSource ?? null, `task ${task.id}`);
72
+ if (task.designWaiver !== null && task.designWaiver !== undefined) requiredText(task.designWaiver, `task ${task.id} designWaiver`);
69
73
  validTimestamp(task.createdAt);
70
74
  validTimestamp(task.updatedAt);
71
75
  }
@@ -120,6 +124,10 @@ export function normalizeTask(task, index, defaultStatus, previousTaskId = null)
120
124
  effort: null,
121
125
  effortSource: null,
122
126
  effortWaiver: null,
127
+ kind: source.kind === undefined || source.kind === null ? null : validTaskKind(source.kind, `tasks[${index}].kind`),
128
+ model: null,
129
+ modelSource: null,
130
+ designWaiver: null,
123
131
  blocker: null,
124
132
  createdAt: timestamp(),
125
133
  updatedAt: timestamp(),
@@ -131,6 +139,20 @@ export function validTaskRoute(value, label = "route") {
131
139
  return value;
132
140
  }
133
141
 
142
+ export function validTaskKind(value, label = "kind") {
143
+ if (!TASK_KINDS.has(value)) throw new Error(`${label} must be one of: ${[...TASK_KINDS].join(", ")}`);
144
+ return value;
145
+ }
146
+
147
+ // A model is a non-empty host-specific string; source "host-option" needs a model, and a model needs a source.
148
+ export function assertModelRecord(model, source, label = "task") {
149
+ if (model !== null) requiredText(model, `${label} model`);
150
+ if (source !== null && !EFFORT_SOURCES.has(source)) throw new Error(`${label} modelSource must be one of: ${[...EFFORT_SOURCES].join(", ")}`);
151
+ if (source === "host-option" && model === null) throw new Error(`${label} modelSource host-option requires a model`);
152
+ if (source === "inherited" && model === null) throw new Error(`${label} modelSource inherited requires a model`);
153
+ if (model !== null && source === null) throw new Error(`${label} model requires a modelSource`);
154
+ }
155
+
134
156
  // effort "inherited" pairs only with source "inherited"; source "host-option" needs a concrete level; a source
135
157
  // without an effort is only meaningful as "not-applied".
136
158
  export function assertEffortRecord(effort, source, label = "task") {
@@ -58,5 +58,5 @@ This skill explicitly instructs the effort level for delegated work. It also ins
58
58
  | 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
59
  | Host without an option | None. | `inherited` |
60
60
 
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.
61
+ - 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`. 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
62
  - 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`, merge the result into the plan's `## Heavy Design`, and bind that plan with `goal verify --kind designRecord`. `--allow-no-design-model <reason>` 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`).
@@ -58,5 +58,5 @@ This skill explicitly instructs the effort level for delegated work. It also ins
58
58
  | 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
59
  | Host without an option | None. | `inherited` |
60
60
 
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.
61
+ - 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`. 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
62
  - 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 (`--allow-no-design-model <reason>` 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
 
@@ -41,7 +41,7 @@ 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. 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.