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.
- package/.codex-plugin/plugin.json +1 -1
- package/docs/model-guidance/README.md +1 -1
- package/docs/model-guidance/claude.md +1 -1
- package/package.json +1 -1
- package/scripts/cairn-goal-cli.mjs +7 -2
- package/scripts/cairn-goal-operations-evidence.mjs +4 -0
- package/scripts/cairn-goal-operations.mjs +34 -4
- package/scripts/cairn-goal-state-validation.mjs +22 -0
- package/skills/cairn-plan/delegation.md +1 -1
- package/skills/cairn-plan/heavy-path.md +2 -1
- package/skills/cairn-work/delegation.md +1 -1
- package/skills/cairn-work/heavy-path.md +1 -0
- package/templates/work-plan.md +1 -1
|
@@ -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
|
@@ -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
|
|
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
|
|
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
|
|
package/templates/work-plan.md
CHANGED
|
@@ -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.
|