@isaacriehm/cairn-core 0.11.9 → 0.13.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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/attention/bulk-accept.js +1 -1
- package/dist/attention/bulk-accept.js.map +1 -1
- package/dist/hooks/runners/session-start.js +44 -6
- package/dist/hooks/runners/session-start.js.map +1 -1
- package/dist/hooks/runners/stop.js +109 -23
- package/dist/hooks/runners/stop.js.map +1 -1
- package/dist/init/sot-emit.d.ts +15 -0
- package/dist/init/sot-emit.js +37 -4
- package/dist/init/sot-emit.js.map +1 -1
- package/dist/mcp/schemas.d.ts +6 -3
- package/dist/mcp/schemas.js +30 -8
- package/dist/mcp/schemas.js.map +1 -1
- package/dist/mcp/tools/decision-get.js +24 -2
- package/dist/mcp/tools/decision-get.js.map +1 -1
- package/dist/mcp/tools/in-scope.js +1 -1
- package/dist/mcp/tools/in-scope.js.map +1 -1
- package/dist/mcp/tools/mission-advance.js +23 -3
- package/dist/mcp/tools/mission-advance.js.map +1 -1
- package/dist/mcp/tools/record-decision.js +34 -3
- package/dist/mcp/tools/record-decision.js.map +1 -1
- package/dist/mcp/tools/resume.js +21 -1
- package/dist/mcp/tools/resume.js.map +1 -1
- package/dist/mcp/tools/search-candidates.d.ts +2 -2
- package/dist/mcp/tools/search-candidates.js +2 -2
- package/dist/mcp/tools/task-complete.d.ts +1 -1
- package/dist/mcp/tools/task-complete.js +30 -4
- package/dist/mcp/tools/task-complete.js.map +1 -1
- package/dist/mcp/tools/task-create.d.ts +1 -1
- package/dist/mcp/tools/task-create.js +18 -3
- package/dist/mcp/tools/task-create.js.map +1 -1
- package/dist/mcp/tools/task-journal-append.js +38 -4
- package/dist/mcp/tools/task-journal-append.js.map +1 -1
- package/dist/missions/task-link.js +60 -1
- package/dist/missions/task-link.js.map +1 -1
- package/dist/session-start/build.js +91 -9
- package/dist/session-start/build.js.map +1 -1
- package/dist/session-start/templates.d.ts +7 -5
- package/dist/session-start/templates.js +20 -25
- package/dist/session-start/templates.js.map +1 -1
- package/dist/status-line/reader.js +86 -3
- package/dist/status-line/reader.js.map +1 -1
- package/dist/tasks/lifecycle.js +15 -1
- package/dist/tasks/lifecycle.js.map +1 -1
- package/package.json +2 -2
|
@@ -18,8 +18,26 @@ import { join } from "node:path";
|
|
|
18
18
|
import { requireBootstrap } from "../bootstrap-guard.js";
|
|
19
19
|
import { mcpError } from "../errors.js";
|
|
20
20
|
import { taskCompleteInput } from "../schemas.js";
|
|
21
|
-
import { completeTask } from "../../tasks/index.js";
|
|
21
|
+
import { completeTask, findCurrentActiveTask } from "../../tasks/index.js";
|
|
22
22
|
import { findActiveMission, readMissionState, readRoadmap, } from "@isaacriehm/cairn-state";
|
|
23
|
+
const SUMMARY_ADVISORY = 2000;
|
|
24
|
+
/**
|
|
25
|
+
* Soft-truncate to `advisory` chars with a trailing marker. Returns
|
|
26
|
+
* the unchanged string when it fits. Splits at the last word boundary
|
|
27
|
+
* before the cap to avoid mid-token truncation.
|
|
28
|
+
*/
|
|
29
|
+
function softTruncate(s, advisory) {
|
|
30
|
+
if (s.length <= advisory)
|
|
31
|
+
return { value: s, truncated: false };
|
|
32
|
+
const removed = s.length - advisory;
|
|
33
|
+
const markerSuffix = `…[+${removed} chars truncated]`;
|
|
34
|
+
const keep = Math.max(0, advisory - markerSuffix.length);
|
|
35
|
+
let cut = s.slice(0, keep);
|
|
36
|
+
const lastSpace = cut.lastIndexOf(" ");
|
|
37
|
+
if (lastSpace > advisory - 200)
|
|
38
|
+
cut = cut.slice(0, lastSpace);
|
|
39
|
+
return { value: cut + markerSuffix, truncated: true };
|
|
40
|
+
}
|
|
23
41
|
/**
|
|
24
42
|
* Read the `title:` and `phase: <terminal>` from a done task's
|
|
25
43
|
* `status.yaml`. Best-effort — returns the task id as title when the
|
|
@@ -164,11 +182,16 @@ async function handler(ctx, input) {
|
|
|
164
182
|
const block = requireBootstrap(ctx.repoRoot);
|
|
165
183
|
if (block !== null)
|
|
166
184
|
return block;
|
|
185
|
+
const taskId = input.task_id ?? findCurrentActiveTask(ctx.repoRoot);
|
|
186
|
+
if (taskId === null) {
|
|
187
|
+
return mcpError("TASK_NOT_FOUND", "no active task — pass task_id explicitly or call cairn_task_create first");
|
|
188
|
+
}
|
|
189
|
+
const summaryResult = input.summary !== undefined ? softTruncate(input.summary, SUMMARY_ADVISORY) : null;
|
|
167
190
|
const result = completeTask({
|
|
168
191
|
repoRoot: ctx.repoRoot,
|
|
169
|
-
taskId
|
|
192
|
+
taskId,
|
|
170
193
|
outcome: input.outcome,
|
|
171
|
-
...(
|
|
194
|
+
...(summaryResult !== null ? { summary: summaryResult.value } : {}),
|
|
172
195
|
source: "cairn_task_complete",
|
|
173
196
|
});
|
|
174
197
|
if (!result.ok) {
|
|
@@ -193,6 +216,7 @@ async function handler(ctx, input) {
|
|
|
193
216
|
outcome: result.outcome,
|
|
194
217
|
completed_at: result.completedAt,
|
|
195
218
|
moved_to: result.movedTo,
|
|
219
|
+
...(summaryResult?.truncated === true ? { truncated: ["summary"] } : {}),
|
|
196
220
|
phase_ready_to_exit: {
|
|
197
221
|
mission_id: phaseReady.mission_id,
|
|
198
222
|
mission_title: phaseReady.mission_title,
|
|
@@ -222,6 +246,7 @@ async function handler(ctx, input) {
|
|
|
222
246
|
outcome: result.outcome,
|
|
223
247
|
completed_at: result.completedAt,
|
|
224
248
|
moved_to: result.movedTo,
|
|
249
|
+
...(summaryResult?.truncated === true ? { truncated: ["summary"] } : {}),
|
|
225
250
|
next_action_hint: nextAction,
|
|
226
251
|
};
|
|
227
252
|
}
|
|
@@ -231,11 +256,12 @@ async function handler(ctx, input) {
|
|
|
231
256
|
outcome: result.outcome,
|
|
232
257
|
completed_at: result.completedAt,
|
|
233
258
|
moved_to: result.movedTo,
|
|
259
|
+
...(summaryResult?.truncated === true ? { truncated: ["summary"] } : {}),
|
|
234
260
|
};
|
|
235
261
|
}
|
|
236
262
|
export const taskCompleteTool = {
|
|
237
263
|
name: "cairn_task_complete",
|
|
238
|
-
description: "Graduate an active task (`.cairn/tasks/active/<task_id>/`) to a terminal phase (succeeded / failed / aborted) and move its directory to `.cairn/tasks/done/`. Called by the reviewer subagent after writing attestation.yaml, by the cairn-direction skill on a confirmed pivot, or by the Stop-hook auto-graduator. Returns TASK_NOT_FOUND if the task was already completed. When the completion satisfies the active phase's exit criteria under `exit_gate=prompt`, the response includes a `phase_ready_to_exit` block carrying a literal `render_instruction` — the caller MUST surface the operator question via `AskUserQuestion` in the same turn before ending. When the task was mission-anchored, succeeded, and no phase-exit prompt fired, the response also carries a `next_action_hint` block telling the model what to do next (continue with the next pending PR in the cursor phase, start the auto-advanced next phase, or end the turn because the mission closed). The hint is the autonomous-continuation contract: the model reads `next_action_hint.instruction` and either calls `cairn_task_create` for the gap or ends the turn cleanly.",
|
|
264
|
+
description: "Graduate an active task (`.cairn/tasks/active/<task_id>/`) to a terminal phase (succeeded / failed / aborted) and move its directory to `.cairn/tasks/done/`. `task_id` is optional — defaults to the most-recently-touched active task. Called by the reviewer subagent after writing attestation.yaml, by the cairn-direction skill on a confirmed pivot, or by the Stop-hook auto-graduator. Returns TASK_NOT_FOUND if the task was already completed or no active task exists. When the completion satisfies the active phase's exit criteria under `exit_gate=prompt`, the response includes a `phase_ready_to_exit` block carrying a literal `render_instruction` — the caller MUST surface the operator question via `AskUserQuestion` in the same turn before ending. When the task was mission-anchored, succeeded, and no phase-exit prompt fired, the response also carries a `next_action_hint` block telling the model what to do next (continue with the next pending PR in the cursor phase, start the auto-advanced next phase, or end the turn because the mission closed). The hint is the autonomous-continuation contract: the model reads `next_action_hint.instruction` and either calls `cairn_task_create` for the gap or ends the turn cleanly.",
|
|
239
265
|
inputSchema: taskCompleteInput,
|
|
240
266
|
handler,
|
|
241
267
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-complete.js","sourceRoot":"","sources":["../../../src/mcp/tools/task-complete.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAC;AAElD,OAAO,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"task-complete.js","sourceRoot":"","sources":["../../../src/mcp/tools/task-complete.ts"],"names":[],"mappings":"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"}
|
|
@@ -6,6 +6,7 @@ import { findActiveMission, readMissionState, readRoadmap, } from "@isaacriehm/c
|
|
|
6
6
|
import { requireBootstrap } from "../bootstrap-guard.js";
|
|
7
7
|
import { mcpError } from "../errors.js";
|
|
8
8
|
import { taskCreateInput } from "../schemas.js";
|
|
9
|
+
import { linkTaskToPhase } from "../../missions/task-link.js";
|
|
9
10
|
/**
|
|
10
11
|
* Generate a `task_id` matching the regex `^TSK-[a-z0-9-]+-[0-9a-f]{7}$`.
|
|
11
12
|
*
|
|
@@ -117,7 +118,7 @@ async function handler(ctx, input) {
|
|
|
117
118
|
status: "ready",
|
|
118
119
|
audience: "dual",
|
|
119
120
|
generated: generatedAt,
|
|
120
|
-
target_path_globs: input.target_path_globs,
|
|
121
|
+
target_path_globs: input.target_path_globs ?? [],
|
|
121
122
|
in_scope_decisions: input.in_scope_decisions ?? [],
|
|
122
123
|
in_scope_invariants: input.in_scope_invariants ?? [],
|
|
123
124
|
needs_review: needsReview,
|
|
@@ -145,7 +146,7 @@ async function handler(ctx, input) {
|
|
|
145
146
|
const statusFrame = {
|
|
146
147
|
id: taskId,
|
|
147
148
|
phase: "running",
|
|
148
|
-
module: input.module ?? input.target_path_globs[0]?.split("/")[0] ?? ".",
|
|
149
|
+
module: input.module ?? input.target_path_globs?.[0]?.split("/")[0] ?? ".",
|
|
149
150
|
title: input.title,
|
|
150
151
|
started_at: generatedAt,
|
|
151
152
|
};
|
|
@@ -156,6 +157,15 @@ async function handler(ctx, input) {
|
|
|
156
157
|
const statusContent = stringifyYaml(statusFrame);
|
|
157
158
|
const statusPath = join(taskDir, "status.yaml");
|
|
158
159
|
writeFileSync(statusPath, statusContent, "utf8");
|
|
160
|
+
// Eagerly link the task to its phase's `task_ids`. Before this, linkage
|
|
161
|
+
// only fired in `onTaskCompleted` — meaning a phase that had pending
|
|
162
|
+
// (not-yet-completed) tasks looked empty to `mission_advance choice=exit`,
|
|
163
|
+
// forcing operators to pass `choice=force` even though real work was
|
|
164
|
+
// anchored to the phase. Linking on create makes the phase ledger
|
|
165
|
+
// reflect intent, not just graduation.
|
|
166
|
+
if (missionId !== null && phaseId !== null) {
|
|
167
|
+
linkTaskToPhase(ctx.repoRoot, missionId, phaseId, taskId);
|
|
168
|
+
}
|
|
159
169
|
return {
|
|
160
170
|
ok: true,
|
|
161
171
|
task_id: taskId,
|
|
@@ -170,7 +180,12 @@ async function handler(ctx, input) {
|
|
|
170
180
|
}
|
|
171
181
|
export const taskCreateTool = {
|
|
172
182
|
name: "cairn_task_create",
|
|
173
|
-
description: "Allocate a task_id and atomically write spec.tightened.md + status.yaml under .cairn/tasks/active/<task_id>/.
|
|
183
|
+
description: "Allocate a task_id and atomically write spec.tightened.md + status.yaml under .cairn/tasks/active/<task_id>/. " +
|
|
184
|
+
"**Required fields**: `slug` (lowercase kebab, 3-80 chars), `title` (≤80 chars), `goal` (free-form). " +
|
|
185
|
+
"**Optional**: `target_path_globs` (defaults to []; pass paths to pin scope), `in_scope_decisions`, `in_scope_invariants`, `constraints`, `out_of_scope`, `acceptance`, `module`, `needs_review`, `mission_id` (anchor to mission; defaults to active mission's cursor — pass `''` to opt out as side-task), `phase_id`. " +
|
|
186
|
+
"Server controls task_id format (`TSK-<slug>-<7-hex>`); callers cannot misformat it. " +
|
|
187
|
+
"Auto-links the new task to its phase's `task_ids` immediately so `cairn_mission_advance choice='exit'` sees the task without waiting for `cairn_task_complete`. " +
|
|
188
|
+
"Required by the cairn-direction skill before any source mutation.",
|
|
174
189
|
inputSchema: taskCreateInput,
|
|
175
190
|
handler,
|
|
176
191
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-create.js","sourceRoot":"","sources":["../../../src/mcp/tools/task-create.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC/D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACrD,OAAO,EAAE,SAAS,IAAI,aAAa,EAAE,MAAM,MAAM,CAAC;AAClD,OAAO,EACL,iBAAiB,EACjB,gBAAgB,EAChB,WAAW,GACZ,MAAM,yBAAyB,CAAC;AAEjC,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"task-create.js","sourceRoot":"","sources":["../../../src/mcp/tools/task-create.ts"],"names":[],"mappings":"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"}
|
|
@@ -13,6 +13,25 @@ import { requireBootstrap } from "../bootstrap-guard.js";
|
|
|
13
13
|
import { mcpError } from "../errors.js";
|
|
14
14
|
import { taskJournalAppendInput } from "../schemas.js";
|
|
15
15
|
import { appendTaskJournal, findCurrentActiveTask } from "../../tasks/index.js";
|
|
16
|
+
const SUMMARY_ADVISORY = 320;
|
|
17
|
+
/**
|
|
18
|
+
* Soft-truncate to `advisory` chars with a trailing marker. Returns the
|
|
19
|
+
* unchanged string when it fits. Splits at the last word boundary
|
|
20
|
+
* before the cap to avoid mid-token truncation.
|
|
21
|
+
*/
|
|
22
|
+
function softTruncate(s, advisory) {
|
|
23
|
+
if (s.length <= advisory)
|
|
24
|
+
return { value: s, truncated: false };
|
|
25
|
+
// Reserve room for the marker so the final string lands at ≤advisory.
|
|
26
|
+
const removed = s.length - advisory;
|
|
27
|
+
const markerSuffix = `…[+${removed} chars truncated]`;
|
|
28
|
+
const keep = Math.max(0, advisory - markerSuffix.length);
|
|
29
|
+
let cut = s.slice(0, keep);
|
|
30
|
+
const lastSpace = cut.lastIndexOf(" ");
|
|
31
|
+
if (lastSpace > advisory - 80)
|
|
32
|
+
cut = cut.slice(0, lastSpace);
|
|
33
|
+
return { value: cut + markerSuffix, truncated: true };
|
|
34
|
+
}
|
|
16
35
|
async function handler(ctx, input) {
|
|
17
36
|
const block = requireBootstrap(ctx.repoRoot);
|
|
18
37
|
if (block !== null)
|
|
@@ -21,23 +40,38 @@ async function handler(ctx, input) {
|
|
|
21
40
|
if (taskId === null) {
|
|
22
41
|
return mcpError("TASK_NOT_FOUND", "no active task — call cairn_task_create first or pass task_id explicitly");
|
|
23
42
|
}
|
|
43
|
+
const summaryResult = softTruncate(input.summary, SUMMARY_ADVISORY);
|
|
44
|
+
const nextStepResult = input.next_step !== undefined ? softTruncate(input.next_step, SUMMARY_ADVISORY) : null;
|
|
24
45
|
const ok = appendTaskJournal({
|
|
25
46
|
repoRoot: ctx.repoRoot,
|
|
26
47
|
taskId,
|
|
27
48
|
sessionId: input.session_id ?? null,
|
|
28
|
-
summary:
|
|
29
|
-
...(
|
|
49
|
+
summary: summaryResult.value,
|
|
50
|
+
...(nextStepResult !== null ? { nextStep: nextStepResult.value } : {}),
|
|
30
51
|
...(input.files_touched !== undefined ? { filesTouched: input.files_touched } : {}),
|
|
31
52
|
...(input.decisions_loaded !== undefined ? { decisionsLoaded: input.decisions_loaded } : {}),
|
|
32
53
|
});
|
|
33
54
|
if (!ok) {
|
|
34
55
|
return mcpError("TASK_NOT_FOUND", `active task directory missing: .cairn/tasks/active/${taskId}/`);
|
|
35
56
|
}
|
|
36
|
-
|
|
57
|
+
const truncatedFields = [];
|
|
58
|
+
if (summaryResult.truncated)
|
|
59
|
+
truncatedFields.push("summary");
|
|
60
|
+
if (nextStepResult?.truncated === true)
|
|
61
|
+
truncatedFields.push("next_step");
|
|
62
|
+
return {
|
|
63
|
+
ok: true,
|
|
64
|
+
task_id: taskId,
|
|
65
|
+
...(truncatedFields.length > 0 ? { truncated: truncatedFields } : {}),
|
|
66
|
+
};
|
|
37
67
|
}
|
|
38
68
|
export const taskJournalAppendTool = {
|
|
39
69
|
name: "cairn_task_journal_append",
|
|
40
|
-
description: "Append one journal entry to the active task's journal.jsonl. Call at the end of every assistant turn while a task is active so that `/cairn-resume <task_id>` after a `/clear` rebuilds the operator's mental state cold.
|
|
70
|
+
description: "Append one journal entry to the active task's journal.jsonl. Call at the end of every assistant turn while a task is active so that `/cairn-resume <task_id>` after a `/clear` rebuilds the operator's mental state cold. " +
|
|
71
|
+
"`summary` is a terse one-liner (~320 chars; soft-truncated above that with a trailing marker — no validation reject). " +
|
|
72
|
+
"`next_step` is what comes next, same length budget. " +
|
|
73
|
+
"`task_id` is optional — Cairn picks the most-recently-touched active task when omitted. " +
|
|
74
|
+
"Response includes `truncated: [field…]` when any input was soft-truncated.",
|
|
41
75
|
inputSchema: taskJournalAppendInput,
|
|
42
76
|
handler,
|
|
43
77
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-journal-append.js","sourceRoot":"","sources":["../../../src/mcp/tools/task-journal-append.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAGH,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAC;AACxC,OAAO,EAAE,sBAAsB,EAAE,MAAM,eAAe,CAAC;AAEvD,OAAO,EAAE,iBAAiB,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAC;AAWhF,KAAK,UAAU,OAAO,CAAC,GAAe,EAAE,KAAY;IAClD,MAAM,KAAK,GAAG,gBAAgB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAC7C,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAEjC,MAAM,MAAM,GAAG,KAAK,CAAC,OAAO,IAAI,qBAAqB,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IACpE,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;QACpB,OAAO,QAAQ,CACb,gBAAgB,EAChB,0EAA0E,CAC3E,CAAC;IACJ,CAAC;IAED,MAAM,EAAE,GAAG,iBAAiB,CAAC;QAC3B,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,MAAM;QACN,SAAS,EAAE,KAAK,CAAC,UAAU,IAAI,IAAI;QACnC,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"task-journal-append.js","sourceRoot":"","sources":["../../../src/mcp/tools/task-journal-append.ts"],"names":[],"mappings":"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"}
|
|
@@ -33,6 +33,50 @@ function taskIsCompletedOnDisk(repoRoot, taskId) {
|
|
|
33
33
|
return false;
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Extract PR-shaped tokens from a phase's `exit_criteria` prose. The
|
|
38
|
+
* pattern matches things like `3.5-MK2`, `3.5-HSH1`, `1.2-AUTH99` —
|
|
39
|
+
* `<digit-cluster>.<digit>-<ALPHA><digits>`. Free-form prose with no
|
|
40
|
+
* matching tokens returns an empty list (caller falls back to plain
|
|
41
|
+
* `allPhaseTasksDone` accounting).
|
|
42
|
+
*/
|
|
43
|
+
const PR_SLUG_RE = /\b\d+\.\d+-[A-Z]+\d+\b/g;
|
|
44
|
+
function extractExitCriteriaPrSlugs(exitCriteria) {
|
|
45
|
+
const matches = exitCriteria.match(PR_SLUG_RE);
|
|
46
|
+
if (matches === null)
|
|
47
|
+
return [];
|
|
48
|
+
const out = [];
|
|
49
|
+
const seen = new Set();
|
|
50
|
+
for (const m of matches) {
|
|
51
|
+
if (seen.has(m))
|
|
52
|
+
continue;
|
|
53
|
+
seen.add(m);
|
|
54
|
+
out.push(m);
|
|
55
|
+
}
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Returns true when any task id covers the named PR slug. Slug `3.5-MK2`
|
|
60
|
+
* is checked two ways: full slug normalized to lower-kebab
|
|
61
|
+
* (`3-5-mk2`) appears as a substring of the task id, OR the bare PR
|
|
62
|
+
* token (`mk2`) appears as a kebab-delimited segment of the task id.
|
|
63
|
+
* The cairn-direction Step 2.6b auto-pick embeds the PR token into
|
|
64
|
+
* the task slug directly, so substring matching is reliable. Case
|
|
65
|
+
* insensitive throughout.
|
|
66
|
+
*/
|
|
67
|
+
function graduatedIdsCoverPrSlug(taskIds, prSlug) {
|
|
68
|
+
const fullKebab = prSlug.replace(/\./g, "-").toLowerCase();
|
|
69
|
+
const bareToken = prSlug.split("-").slice(1).join("-").toLowerCase();
|
|
70
|
+
for (const id of taskIds) {
|
|
71
|
+
const lower = id.toLowerCase();
|
|
72
|
+
if (lower.includes(fullKebab))
|
|
73
|
+
return true;
|
|
74
|
+
if (bareToken.length > 0 && new RegExp(`(^|-)${bareToken}(-|$)`).test(lower)) {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
36
80
|
import { appendMissionJournal, effectivePhaseExitGate, readMissionState, readRoadmap, writeMissionState, } from "@isaacriehm/cairn-state";
|
|
37
81
|
import { writeInvalidationEvent } from "../events/index.js";
|
|
38
82
|
import { advancePhase, allPhaseTasksDone } from "./cursor.js";
|
|
@@ -119,6 +163,22 @@ export function onTaskCompleted(repoRoot, taskId, outcome, taskIsDone = (id) =>
|
|
|
119
163
|
if (!allPhaseTasksDone(state, anchor.phase_id, taskIsDone)) {
|
|
120
164
|
return { kind: "linked", mission_id: anchor.mission_id, phase_id: anchor.phase_id, gate };
|
|
121
165
|
}
|
|
166
|
+
// Cross-check: if the phase's exit_criteria enumerates PR slugs
|
|
167
|
+
// (e.g. `3.5-MK2`, `3.5-MK3`), refuse to fire phase-ready until a
|
|
168
|
+
// task has graduated for each named PR. Counting `task_ids.length`
|
|
169
|
+
// alone passes false-positive when only some PRs ever got a task
|
|
170
|
+
// (bug-mine report #13). Free-form exit criteria with no PR slugs
|
|
171
|
+
// skip this check — `allPhaseTasksDone` is authoritative there.
|
|
172
|
+
const phaseDef = roadmap.frontmatter.phases.find((p) => p.id === anchor.phase_id);
|
|
173
|
+
const exitCriteriaText = phaseDef?.exit_criteria ?? "";
|
|
174
|
+
const prSlugs = extractExitCriteriaPrSlugs(exitCriteriaText);
|
|
175
|
+
if (prSlugs.length > 0) {
|
|
176
|
+
const graduatedIds = state.phase_progress[anchor.phase_id]?.task_ids ?? [];
|
|
177
|
+
const missing = prSlugs.filter((slug) => !graduatedIdsCoverPrSlug(graduatedIds, slug));
|
|
178
|
+
if (missing.length > 0) {
|
|
179
|
+
return { kind: "linked", mission_id: anchor.mission_id, phase_id: anchor.phase_id, gate };
|
|
180
|
+
}
|
|
181
|
+
}
|
|
122
182
|
if (gate === "auto") {
|
|
123
183
|
const r = advancePhase(repoRoot, anchor.mission_id, anchor.phase_id);
|
|
124
184
|
if (r.ok) {
|
|
@@ -162,7 +222,6 @@ export function onTaskCompleted(repoRoot, taskId, outcome, taskIsDone = (id) =>
|
|
|
162
222
|
// best-effort — at worst we re-emit on the next task completion
|
|
163
223
|
}
|
|
164
224
|
}
|
|
165
|
-
const phaseDef = roadmap.frontmatter.phases.find((p) => p.id === anchor.phase_id);
|
|
166
225
|
return {
|
|
167
226
|
kind: "phase-ready-to-exit",
|
|
168
227
|
mission_id: anchor.mission_id,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-link.js","sourceRoot":"","sources":["../../src/missions/task-link.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"task-link.js","sourceRoot":"","sources":["../../src/missions/task-link.ts"],"names":[],"mappings":"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"}
|
|
@@ -276,14 +276,28 @@ function renderActiveMissionSection(repoRoot, warnings) {
|
|
|
276
276
|
// operator gets the prompt back after `/clear` (the Stop-hook
|
|
277
277
|
// pending file is session-scoped + consume-once).
|
|
278
278
|
const gate = effectivePhaseExitGate(roadmap.frontmatter, cursorPhase.id);
|
|
279
|
+
const tasksDone = taskIds.length > 0 &&
|
|
280
|
+
allPhaseTasksDone(state, cursorPhase.id, (id) => existsSync(join(repoRoot, ".cairn", "tasks", "done", id)));
|
|
281
|
+
// PR-slug cross-check: if exit_criteria enumerates PR refs, every
|
|
282
|
+
// ref must have a graduated task before phase-ready fires (bug-mine
|
|
283
|
+
// report #13). Falls through cleanly when exit_criteria is free-form
|
|
284
|
+
// prose with no PR-shaped tokens.
|
|
285
|
+
const prCoverage = computePhasePrCoverage(cursorPhase.exit_criteria ?? "", taskIds);
|
|
279
286
|
const phaseReady = gate === "prompt" &&
|
|
280
|
-
|
|
281
|
-
|
|
287
|
+
tasksDone &&
|
|
288
|
+
prCoverage.missing.length === 0 &&
|
|
282
289
|
!isMissionPhaseDeferActive(repoRoot, missionId, cursorPhase.id);
|
|
283
290
|
if (phaseReady) {
|
|
284
291
|
lines.push("");
|
|
285
292
|
lines.push(`**Phase ready to exit** — all ${taskIds.length} linked task(s) graduated. Use \`AskUserQuestion\` to confirm, then call \`cairn_mission_advance({phase_id: "${cursorPhase.id}", choice: "exit"})\` to graduate, or \`choice: "not_yet"\` / \`"defer"\` to keep the cursor.`);
|
|
286
293
|
}
|
|
294
|
+
else if (gate === "prompt" &&
|
|
295
|
+
tasksDone &&
|
|
296
|
+
prCoverage.missing.length > 0 &&
|
|
297
|
+
!isMissionPhaseDeferActive(repoRoot, missionId, cursorPhase.id)) {
|
|
298
|
+
lines.push("");
|
|
299
|
+
lines.push(`**Phase tasks graduated but exit_criteria PR refs missing**: ${prCoverage.missing.map((s) => `\`${s}\``).join(", ")}. Phase-exit prompt held; ship the named PRs first.`);
|
|
300
|
+
}
|
|
287
301
|
}
|
|
288
302
|
else {
|
|
289
303
|
lines.push(`- cursor phase: (none — mission ready to close)`);
|
|
@@ -297,6 +311,54 @@ function renderActiveMissionSection(repoRoot, warnings) {
|
|
|
297
311
|
lines.push("Tasks under this cursor inherit `mission_id` + `phase_id` automatically. Side-tasks (regression fixes, refactors unrelated to the phase exit_criteria) must pass `mission_id: \"\"` to `cairn_task_create` so they don't pollute `phase_progress.task_ids`.");
|
|
298
312
|
return lines.join("\n");
|
|
299
313
|
}
|
|
314
|
+
/**
|
|
315
|
+
* Returns the PR slugs named in `exitCriteria` (e.g. `3.5-MK2`,
|
|
316
|
+
* `3.5-MK3`) along with the subset that is NOT covered by any
|
|
317
|
+
* graduated task id. Matching is case-insensitive — the bare PR token
|
|
318
|
+
* (`mk2`) must appear as a kebab-delimited segment OR the full
|
|
319
|
+
* dot-replaced slug (`3-5-mk2`) must appear as a substring of the
|
|
320
|
+
* task id. The cairn-direction Step 2.6b auto-pick embeds the PR
|
|
321
|
+
* token into the task slug directly.
|
|
322
|
+
*
|
|
323
|
+
* Free-form `exitCriteria` with no PR-shaped tokens returns
|
|
324
|
+
* `{ slugs: [], missing: [] }` — caller treats this as "no PR
|
|
325
|
+
* accounting needed" and falls back to plain task-count logic.
|
|
326
|
+
*/
|
|
327
|
+
const PHASE_PR_SLUG_RE = /\b\d+\.\d+-[A-Z]+\d+\b/g;
|
|
328
|
+
function computePhasePrCoverage(exitCriteria, taskIds) {
|
|
329
|
+
const matches = exitCriteria.match(PHASE_PR_SLUG_RE);
|
|
330
|
+
if (matches === null || matches.length === 0) {
|
|
331
|
+
return { slugs: [], missing: [] };
|
|
332
|
+
}
|
|
333
|
+
const seen = new Set();
|
|
334
|
+
const slugs = [];
|
|
335
|
+
for (const m of matches) {
|
|
336
|
+
if (seen.has(m))
|
|
337
|
+
continue;
|
|
338
|
+
seen.add(m);
|
|
339
|
+
slugs.push(m);
|
|
340
|
+
}
|
|
341
|
+
const missing = [];
|
|
342
|
+
for (const slug of slugs) {
|
|
343
|
+
if (!taskIdsCoverPrSlug(taskIds, slug))
|
|
344
|
+
missing.push(slug);
|
|
345
|
+
}
|
|
346
|
+
return { slugs, missing };
|
|
347
|
+
}
|
|
348
|
+
function taskIdsCoverPrSlug(taskIds, prSlug) {
|
|
349
|
+
const fullKebab = prSlug.replace(/\./g, "-").toLowerCase();
|
|
350
|
+
const bareToken = prSlug.split("-").slice(1).join("-").toLowerCase();
|
|
351
|
+
for (const id of taskIds) {
|
|
352
|
+
const lower = id.toLowerCase();
|
|
353
|
+
if (lower.includes(fullKebab))
|
|
354
|
+
return true;
|
|
355
|
+
if (bareToken.length > 0 &&
|
|
356
|
+
new RegExp(`(^|-)${bareToken}(-|$)`).test(lower)) {
|
|
357
|
+
return true;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
return false;
|
|
361
|
+
}
|
|
300
362
|
function isMissionPhaseDeferActive(repoRoot, missionId, phaseId) {
|
|
301
363
|
const path = join(repoRoot, ".cairn", ".mission-phase-deferred-until");
|
|
302
364
|
if (!existsSync(path))
|
|
@@ -328,7 +390,7 @@ function isMissionPhaseDeferActive(repoRoot, missionId, phaseId) {
|
|
|
328
390
|
function readBrandAndPositioning(repoRoot, warnings) {
|
|
329
391
|
const brandPath = join(repoRoot, ".cairn", "ground", "brand", "overview.md");
|
|
330
392
|
const positioningPath = join(repoRoot, ".cairn", "ground", "product", "positioning.md");
|
|
331
|
-
const
|
|
393
|
+
const sections = [];
|
|
332
394
|
for (const [label, path] of [
|
|
333
395
|
["Brand overview", brandPath],
|
|
334
396
|
["Product positioning", positioningPath],
|
|
@@ -346,14 +408,32 @@ function readBrandAndPositioning(repoRoot, warnings) {
|
|
|
346
408
|
const draftHint = status === "draft"
|
|
347
409
|
? " [DRAFT — operator has not filled this in; ask before making design decisions]"
|
|
348
410
|
: "";
|
|
349
|
-
|
|
411
|
+
sections.push({ label, body, draftHint });
|
|
350
412
|
}
|
|
351
413
|
catch (err) {
|
|
352
414
|
warnings.push(`${label} read failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
353
415
|
}
|
|
354
416
|
}
|
|
355
|
-
if (
|
|
417
|
+
if (sections.length === 0)
|
|
356
418
|
return null;
|
|
419
|
+
// Dedupe by normalized body. Strip leading `#`-headings + collapse
|
|
420
|
+
// whitespace so `# Brand overview\n\nfoo` and `# Product positioning\n\nfoo`
|
|
421
|
+
// are recognized as identical prose under different titles. Phase
|
|
422
|
+
// 6-brand pre-fills both files from the same Q1 answer; in practice
|
|
423
|
+
// adopted repos ship a SessionStart with the same paragraph appearing
|
|
424
|
+
// twice under two headings until the operator manually diverges them.
|
|
425
|
+
// Render once with a merged label.
|
|
426
|
+
const normalize = (s) => s
|
|
427
|
+
.replace(/^\s*#{1,6}\s+.*$/gm, "") // drop H1-H6 lines entirely
|
|
428
|
+
.replace(/\s+/g, " ")
|
|
429
|
+
.trim();
|
|
430
|
+
const uniqBodies = new Set(sections.map((s) => normalize(s.body)));
|
|
431
|
+
if (uniqBodies.size === 1 && sections.length > 1) {
|
|
432
|
+
const first = sections[0];
|
|
433
|
+
const mergedLabel = sections.map((s) => s.label).join(" / ");
|
|
434
|
+
return `## Brand and product context\n\n### ${mergedLabel}${first.draftHint}\n\n${first.body}`;
|
|
435
|
+
}
|
|
436
|
+
const parts = sections.map((s) => `### ${s.label}${s.draftHint}\n\n${s.body}`);
|
|
357
437
|
return `## Brand and product context\n\n${parts.join("\n\n")}`;
|
|
358
438
|
}
|
|
359
439
|
function computeTotalChars(sections) {
|
|
@@ -435,8 +515,10 @@ function renderGroundStateSummary(decisionsCount, invariantsCount) {
|
|
|
435
515
|
lines.push("Bare `§DEC-NNNN` and `§INV-NNNN` citations in source files resolve " +
|
|
436
516
|
"automatically when you Read them — the PostToolUse(Read) hook " +
|
|
437
517
|
"prepends a legend with each citation's title + status. Use " +
|
|
438
|
-
"`
|
|
439
|
-
"for
|
|
518
|
+
"`cairn_in_scope({path_globs, types?})` for path-targeted lookups " +
|
|
519
|
+
"(omit `types` for both DECs + INVs, or filter by " +
|
|
520
|
+
"`types: [\"decision\"]` / `[\"invariant\"]`), `cairn_search(query)` " +
|
|
521
|
+
"for free-text.");
|
|
440
522
|
return lines.join("\n");
|
|
441
523
|
}
|
|
442
524
|
function renderDecisionsSection(decisions) {
|
|
@@ -453,7 +535,7 @@ function renderDecisionsSection(decisions) {
|
|
|
453
535
|
lines.push(` status: ${d.status}; scope: ${scope}; supersedes: ${supersedes}`);
|
|
454
536
|
}
|
|
455
537
|
if (decisions.length > DECISIONS_CAP) {
|
|
456
|
-
lines.push(`…${decisions.length - DECISIONS_CAP} additional decision${decisions.length - DECISIONS_CAP === 1 ? "" : "s"} — call \`
|
|
538
|
+
lines.push(`…${decisions.length - DECISIONS_CAP} additional decision${decisions.length - DECISIONS_CAP === 1 ? "" : "s"} — call \`cairn_in_scope({path_globs, types: ["decision"]})\` for the rest.`);
|
|
457
539
|
}
|
|
458
540
|
return lines.join("\n");
|
|
459
541
|
}
|
|
@@ -468,7 +550,7 @@ function renderInvariantsSection(invariants) {
|
|
|
468
550
|
lines.push(`- **${inv.id}** — ${inv.title} source_decision: ${inv.source_decision ?? "(none)"}`);
|
|
469
551
|
}
|
|
470
552
|
if (invariants.length > INVARIANTS_CAP) {
|
|
471
|
-
lines.push(`…${invariants.length - INVARIANTS_CAP} additional — call \`
|
|
553
|
+
lines.push(`…${invariants.length - INVARIANTS_CAP} additional — call \`cairn_in_scope({path_globs, types: ["invariant"]})\`.`);
|
|
472
554
|
}
|
|
473
555
|
return lines.join("\n");
|
|
474
556
|
}
|