@isaacriehm/cairn-core 0.13.3 → 0.14.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/dist/.tsbuildinfo +1 -1
- package/dist/gc/entity-orphan.d.ts +55 -0
- package/dist/gc/entity-orphan.js +171 -0
- package/dist/gc/entity-orphan.js.map +1 -0
- package/dist/gc/index.d.ts +4 -0
- package/dist/gc/index.js +2 -0
- package/dist/gc/index.js.map +1 -1
- package/dist/gc/retire.d.ts +57 -0
- package/dist/gc/retire.js +189 -0
- package/dist/gc/retire.js.map +1 -0
- package/dist/gc/sweep.js +11 -0
- package/dist/gc/sweep.js.map +1 -1
- package/dist/gc/types.d.ts +2 -2
- package/dist/hooks/ask-user-blocked.d.ts +8 -0
- package/dist/hooks/ask-user-blocked.js +13 -0
- package/dist/hooks/ask-user-blocked.js.map +1 -0
- package/dist/hooks/post-tool-use/ask-user-blocked.d.ts +18 -0
- package/dist/hooks/post-tool-use/ask-user-blocked.js +113 -0
- package/dist/hooks/post-tool-use/ask-user-blocked.js.map +1 -0
- package/dist/hooks/post-tool-use/index.d.ts +1 -0
- package/dist/hooks/post-tool-use/index.js +1 -0
- package/dist/hooks/post-tool-use/index.js.map +1 -1
- package/dist/hooks/runners/context-threshold.js +2 -2
- package/dist/hooks/runners/context-threshold.js.map +1 -1
- package/dist/hooks/runners/gc-autotrigger.d.ts +7 -4
- package/dist/hooks/runners/gc-autotrigger.js +7 -4
- package/dist/hooks/runners/gc-autotrigger.js.map +1 -1
- package/dist/hooks/runners/stop.js +173 -90
- package/dist/hooks/runners/stop.js.map +1 -1
- package/dist/init/ingest-docs.d.ts +2 -4
- package/dist/init/ingest-docs.js +2 -4
- package/dist/init/ingest-docs.js.map +1 -1
- package/dist/mcp/errors.d.ts +1 -1
- package/dist/mcp/errors.js.map +1 -1
- package/dist/mcp/schemas.d.ts +7 -94
- package/dist/mcp/schemas.js +23 -87
- package/dist/mcp/schemas.js.map +1 -1
- package/dist/mcp/serve.js +8 -6
- package/dist/mcp/serve.js.map +1 -1
- package/dist/mcp/tools/index.js +6 -29
- package/dist/mcp/tools/index.js.map +1 -1
- package/dist/mcp/tools/retire-entity.d.ts +22 -0
- package/dist/mcp/tools/retire-entity.js +62 -0
- package/dist/mcp/tools/retire-entity.js.map +1 -0
- package/dist/mcp/tools/task-complete.js +7 -19
- package/dist/mcp/tools/task-complete.js.map +1 -1
- package/dist/mcp/tools/task-create.d.ts +0 -1
- package/dist/mcp/tools/task-create.js +31 -10
- package/dist/mcp/tools/task-create.js.map +1 -1
- package/dist/mcp/tools/task-journal-append.js +23 -2
- package/dist/mcp/tools/task-journal-append.js.map +1 -1
- package/dist/session-start/build.d.ts +26 -0
- package/dist/session-start/build.js +30 -0
- package/dist/session-start/build.js.map +1 -1
- package/dist/session-start/index.d.ts +1 -1
- package/dist/session-start/index.js +1 -1
- package/dist/session-start/index.js.map +1 -1
- package/dist/tasks/index.d.ts +2 -2
- package/dist/tasks/index.js +1 -1
- package/dist/tasks/index.js.map +1 -1
- package/dist/tasks/lifecycle.d.ts +14 -22
- package/dist/tasks/lifecycle.js +14 -47
- package/dist/tasks/lifecycle.js.map +1 -1
- package/package.json +2 -2
- package/dist/mcp/tools/align-drain.d.ts +0 -7
- package/dist/mcp/tools/align-drain.js +0 -26
- package/dist/mcp/tools/align-drain.js.map +0 -1
- package/dist/mcp/tools/archive.d.ts +0 -8
- package/dist/mcp/tools/archive.js +0 -72
- package/dist/mcp/tools/archive.js.map +0 -1
- package/dist/mcp/tools/attention-restore.d.ts +0 -14
- package/dist/mcp/tools/attention-restore.js +0 -22
- package/dist/mcp/tools/attention-restore.js.map +0 -1
- package/dist/mcp/tools/decisions-for-symbol.d.ts +0 -7
- package/dist/mcp/tools/decisions-for-symbol.js +0 -42
- package/dist/mcp/tools/decisions-for-symbol.js.map +0 -1
- package/dist/mcp/tools/get-full.d.ts +0 -7
- package/dist/mcp/tools/get-full.js +0 -46
- package/dist/mcp/tools/get-full.js.map +0 -1
- package/dist/mcp/tools/ground-get.d.ts +0 -7
- package/dist/mcp/tools/ground-get.js +0 -77
- package/dist/mcp/tools/ground-get.js.map +0 -1
- package/dist/mcp/tools/mission-close.d.ts +0 -8
- package/dist/mcp/tools/mission-close.js +0 -53
- package/dist/mcp/tools/mission-close.js.map +0 -1
- package/dist/mcp/tools/mission-reopen.d.ts +0 -13
- package/dist/mcp/tools/mission-reopen.js +0 -56
- package/dist/mcp/tools/mission-reopen.js.map +0 -1
- package/dist/mcp/tools/reject-candidate.d.ts +0 -24
- package/dist/mcp/tools/reject-candidate.js +0 -71
- package/dist/mcp/tools/reject-candidate.js.map +0 -1
- package/dist/mcp/tools/search-candidates.d.ts +0 -20
- package/dist/mcp/tools/search-candidates.js +0 -93
- package/dist/mcp/tools/search-candidates.js.map +0 -1
- package/dist/mcp/tools/supersedes-chain.d.ts +0 -6
- package/dist/mcp/tools/supersedes-chain.js +0 -66
- package/dist/mcp/tools/supersedes-chain.js.map +0 -1
- package/dist/mcp/tools/timeline.d.ts +0 -9
- package/dist/mcp/tools/timeline.js +0 -61
- package/dist/mcp/tools/timeline.js.map +0 -1
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostToolUse hook on `AskUserQuestion` — auto-stamps the current active
|
|
3
|
+
* task with `blocked_on: operator` in `.cairn/tasks/active/<id>/status.yaml`
|
|
4
|
+
* so the Stop hook's stalled-task scanner skips it (the work can't
|
|
5
|
+
* progress until the operator answers).
|
|
6
|
+
*
|
|
7
|
+
* Wired into `bug-mine 0.13.8` / Phase 5. Producer pair for the
|
|
8
|
+
* existing `blocked_on: operator` skip rule in
|
|
9
|
+
* `runners/stop.ts:scanStalledRunningTasks`.
|
|
10
|
+
*
|
|
11
|
+
* No-op when:
|
|
12
|
+
* - `tool_name !== "AskUserQuestion"` (manifest matcher narrows but
|
|
13
|
+
* defense-in-depth).
|
|
14
|
+
* - No active task in `.cairn/tasks/active/`.
|
|
15
|
+
* - status.yaml missing or unparseable.
|
|
16
|
+
* - `blocked_on: operator` already present (idempotent).
|
|
17
|
+
*/
|
|
18
|
+
export declare function runAskUserBlockedHook(): Promise<void>;
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostToolUse hook on `AskUserQuestion` — auto-stamps the current active
|
|
3
|
+
* task with `blocked_on: operator` in `.cairn/tasks/active/<id>/status.yaml`
|
|
4
|
+
* so the Stop hook's stalled-task scanner skips it (the work can't
|
|
5
|
+
* progress until the operator answers).
|
|
6
|
+
*
|
|
7
|
+
* Wired into `bug-mine 0.13.8` / Phase 5. Producer pair for the
|
|
8
|
+
* existing `blocked_on: operator` skip rule in
|
|
9
|
+
* `runners/stop.ts:scanStalledRunningTasks`.
|
|
10
|
+
*
|
|
11
|
+
* No-op when:
|
|
12
|
+
* - `tool_name !== "AskUserQuestion"` (manifest matcher narrows but
|
|
13
|
+
* defense-in-depth).
|
|
14
|
+
* - No active task in `.cairn/tasks/active/`.
|
|
15
|
+
* - status.yaml missing or unparseable.
|
|
16
|
+
* - `blocked_on: operator` already present (idempotent).
|
|
17
|
+
*/
|
|
18
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
19
|
+
import { join } from "node:path";
|
|
20
|
+
import { z } from "zod";
|
|
21
|
+
import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
|
|
22
|
+
import { resolveRepoRoot } from "../../session-start/index.js";
|
|
23
|
+
import { findCurrentActiveTask } from "../../tasks/index.js";
|
|
24
|
+
import { readHookStdin } from "../runners/payload.js";
|
|
25
|
+
import { logger } from "../../logger.js";
|
|
26
|
+
const log = logger("hooks.post-tool-use.ask-user-blocked");
|
|
27
|
+
const PayloadSchema = z
|
|
28
|
+
.object({
|
|
29
|
+
session_id: z.string().optional(),
|
|
30
|
+
transcript_path: z.string().optional(),
|
|
31
|
+
cwd: z.string().optional(),
|
|
32
|
+
tool_name: z.string().optional(),
|
|
33
|
+
})
|
|
34
|
+
.passthrough();
|
|
35
|
+
function parsePayload(text) {
|
|
36
|
+
if (text.trim().length === 0)
|
|
37
|
+
return {};
|
|
38
|
+
try {
|
|
39
|
+
const raw = JSON.parse(text);
|
|
40
|
+
const result = PayloadSchema.safeParse(raw);
|
|
41
|
+
return result.success ? result.data : {};
|
|
42
|
+
}
|
|
43
|
+
catch {
|
|
44
|
+
return {};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function emitShapeB(additionalContext) {
|
|
48
|
+
const out = {
|
|
49
|
+
continue: true,
|
|
50
|
+
hookSpecificOutput: {
|
|
51
|
+
hookEventName: "PostToolUse",
|
|
52
|
+
additionalContext,
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
process.stdout.write(JSON.stringify(out));
|
|
56
|
+
process.stdout.write("\n");
|
|
57
|
+
}
|
|
58
|
+
export async function runAskUserBlockedHook() {
|
|
59
|
+
try {
|
|
60
|
+
const raw = await readHookStdin();
|
|
61
|
+
const payload = parsePayload(raw);
|
|
62
|
+
if (payload.tool_name !== "AskUserQuestion") {
|
|
63
|
+
emitShapeB("");
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const cwd = payload.cwd ?? process.cwd();
|
|
67
|
+
const repoRoot = resolveRepoRoot(cwd);
|
|
68
|
+
if (repoRoot === null) {
|
|
69
|
+
emitShapeB("");
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const taskId = findCurrentActiveTask(repoRoot);
|
|
73
|
+
if (taskId === null) {
|
|
74
|
+
emitShapeB("");
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
const statusPath = join(repoRoot, ".cairn", "tasks", "active", taskId, "status.yaml");
|
|
78
|
+
if (!existsSync(statusPath)) {
|
|
79
|
+
emitShapeB("");
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
let parsed;
|
|
83
|
+
try {
|
|
84
|
+
parsed = parseYaml(readFileSync(statusPath, "utf8"));
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
emitShapeB("");
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
91
|
+
emitShapeB("");
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
const status = parsed;
|
|
95
|
+
if (status["blocked_on"] === "operator") {
|
|
96
|
+
emitShapeB("");
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
status["blocked_on"] = "operator";
|
|
100
|
+
try {
|
|
101
|
+
writeFileSync(statusPath, stringifyYaml(status), "utf8");
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
log.warn({ err: err instanceof Error ? err.message : String(err), taskId }, "ask-user-blocked: write to status.yaml failed");
|
|
105
|
+
}
|
|
106
|
+
emitShapeB("");
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
log.warn({ err: err instanceof Error ? err.message : String(err) }, "ask-user-blocked hook failed; degrading to no-op");
|
|
110
|
+
emitShapeB("");
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=ask-user-blocked.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ask-user-blocked.js","sourceRoot":"","sources":["../../../src/hooks/post-tool-use/ask-user-blocked.ts"],"names":[],"mappings":"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"}
|
|
@@ -18,4 +18,5 @@ export type { CopySafetyConfig } from "./allowlist-reader.js";
|
|
|
18
18
|
export { alignFile, runSotAlign, executeSotAlign } from "./sot-align.js";
|
|
19
19
|
export type { AlignFileArgs, AlignFileResult, CreationVerdict, DedupVerdict, } from "./sot-align.js";
|
|
20
20
|
export { runPostWriteHook } from "./post-write.js";
|
|
21
|
+
export { runAskUserBlockedHook } from "./ask-user-blocked.js";
|
|
21
22
|
export { containsEssayClassShape, isMarkdownPath } from "../sot-align-common.js";
|
|
@@ -13,5 +13,6 @@ export { scanForCopyLeakage } from "./copy-scanner.js";
|
|
|
13
13
|
export { readCopySafetyConfig } from "./allowlist-reader.js";
|
|
14
14
|
export { alignFile, runSotAlign, executeSotAlign } from "./sot-align.js";
|
|
15
15
|
export { runPostWriteHook } from "./post-write.js";
|
|
16
|
+
export { runAskUserBlockedHook } from "./ask-user-blocked.js";
|
|
16
17
|
export { containsEssayClassShape, isMarkdownPath } from "../sot-align-common.js";
|
|
17
18
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/hooks/post-tool-use/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAElD,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAE7D,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAOzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,uBAAuB,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/hooks/post-tool-use/index.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AAEtD,OAAO,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAElD,OAAO,EAAE,gBAAgB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAC7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAC;AAE7D,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAOzE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -139,7 +139,7 @@ export function renderContextThresholdHint(hit, taskId) {
|
|
|
139
139
|
...header,
|
|
140
140
|
"No active task — context climbed through general work, or the active task just graduated this tick. Nothing to resume from.",
|
|
141
141
|
"",
|
|
142
|
-
"
|
|
142
|
+
"When you reach a stopping point, surface to operator (e.g. via `AskUserQuestion`); if mid-flow, finish first:",
|
|
143
143
|
"",
|
|
144
144
|
"> Context at " + hit.pct + "% of window. Pick:",
|
|
145
145
|
"> ",
|
|
@@ -153,7 +153,7 @@ export function renderContextThresholdHint(hit, taskId) {
|
|
|
153
153
|
...header,
|
|
154
154
|
`Active task: \`${taskId}\`.`,
|
|
155
155
|
"",
|
|
156
|
-
"
|
|
156
|
+
"When you reach a stopping point, surface to operator (e.g. via `AskUserQuestion`); if mid-flow, finish first:",
|
|
157
157
|
"",
|
|
158
158
|
"> Context at " + hit.pct + "% of window. Pick:",
|
|
159
159
|
"> ",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-threshold.js","sourceRoot":"","sources":["../../../src/hooks/runners/context-threshold.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"context-threshold.js","sourceRoot":"","sources":["../../../src/hooks/runners/context-threshold.ts"],"names":[],"mappings":"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"}
|
|
@@ -8,10 +8,13 @@
|
|
|
8
8
|
* wait for the spawned process — it stamps the marker, fires the spawn,
|
|
9
9
|
* unrefs, and returns.
|
|
10
10
|
*
|
|
11
|
-
* `cairn gc sweep`
|
|
12
|
-
* `cairn-attention` for operator triage.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* The spawned `cairn gc sweep` surfaces all detection passes through
|
|
12
|
+
* `cairn-attention` for operator triage (no commit). Because the spawn
|
|
13
|
+
* carries `CAIRN_GC_AUTOTRIGGERED=1`, that same process additionally
|
|
14
|
+
* auto-retires the SAFE entity-orphan subset (`runEntityRetire({ apply })`,
|
|
15
|
+
* canary-gated, rolled back on failure) — the one autonomous mutation in
|
|
16
|
+
* the daily tick. Every other pass's proposals stay operator-driven via
|
|
17
|
+
* `cairn gc run --apply-classes` until proven safe in the field.
|
|
15
18
|
*/
|
|
16
19
|
export interface GcAutotriggerOptions {
|
|
17
20
|
repoRoot: string;
|
|
@@ -8,10 +8,13 @@
|
|
|
8
8
|
* wait for the spawned process — it stamps the marker, fires the spawn,
|
|
9
9
|
* unrefs, and returns.
|
|
10
10
|
*
|
|
11
|
-
* `cairn gc sweep`
|
|
12
|
-
* `cairn-attention` for operator triage.
|
|
13
|
-
*
|
|
14
|
-
*
|
|
11
|
+
* The spawned `cairn gc sweep` surfaces all detection passes through
|
|
12
|
+
* `cairn-attention` for operator triage (no commit). Because the spawn
|
|
13
|
+
* carries `CAIRN_GC_AUTOTRIGGERED=1`, that same process additionally
|
|
14
|
+
* auto-retires the SAFE entity-orphan subset (`runEntityRetire({ apply })`,
|
|
15
|
+
* canary-gated, rolled back on failure) — the one autonomous mutation in
|
|
16
|
+
* the daily tick. Every other pass's proposals stay operator-driven via
|
|
17
|
+
* `cairn gc run --apply-classes` until proven safe in the field.
|
|
15
18
|
*/
|
|
16
19
|
import { spawn } from "node:child_process";
|
|
17
20
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"gc-autotrigger.js","sourceRoot":"","sources":["../../../src/hooks/runners/gc-autotrigger.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"gc-autotrigger.js","sourceRoot":"","sources":["../../../src/hooks/runners/gc-autotrigger.ts"],"names":[],"mappings":"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"}
|
|
@@ -22,7 +22,7 @@ import { isDeferActive, readDeferState } from "../defer.js";
|
|
|
22
22
|
import { resolveRepoRoot } from "../../session-start/index.js";
|
|
23
23
|
import { readEventsMarker, stampEventsPoll, } from "../../session/index.js";
|
|
24
24
|
import { writeStatusJson } from "../../status-line/index.js";
|
|
25
|
-
import { completeTask, findCurrentActiveTask, readTaskAttestationState,
|
|
25
|
+
import { completeTask, findCurrentActiveTask, readTaskAttestationState, } from "../../tasks/index.js";
|
|
26
26
|
import { effectivePhaseExitGate, findActiveMission, readMissionState, readRoadmap, } from "@isaacriehm/cairn-state";
|
|
27
27
|
import { checkContextThreshold, renderContextThresholdHint, } from "./context-threshold.js";
|
|
28
28
|
import { writePhaseReadyPending, } from "./phase-ready-surface.js";
|
|
@@ -66,7 +66,7 @@ const MAX_REASON_CHARS = 4_000;
|
|
|
66
66
|
* CC convention, not a failure signal. One short line keeps the chat
|
|
67
67
|
* tidy without dropping the cue entirely.
|
|
68
68
|
*/
|
|
69
|
-
const REASON_PREAMBLE = "↳ Cairn
|
|
69
|
+
const REASON_PREAMBLE = "↳ Cairn hint — surface to operator at a natural stopping point. Don't interrupt productive work; quote / `AskUserQuestion` only when there's no obvious continuation.\n\n";
|
|
70
70
|
function clampReason(body) {
|
|
71
71
|
if (body.length === 0)
|
|
72
72
|
return body;
|
|
@@ -85,6 +85,96 @@ function clampReason(body) {
|
|
|
85
85
|
* the window or whenever the payload hash changes.
|
|
86
86
|
*/
|
|
87
87
|
const CUE_DEBOUNCE_WINDOW_MS = 60 * 60 * 1000;
|
|
88
|
+
/**
|
|
89
|
+
* Bug-mine 0.13.8 — Phase 5 stall-cue tuning.
|
|
90
|
+
*
|
|
91
|
+
* `SESSION_ACTIVITY_WINDOW_MS` — when the transcript records any
|
|
92
|
+
* `tool_use` within this window, the AI is actively working; suppress
|
|
93
|
+
* the stalled-task surface so the cue doesn't interrupt productive
|
|
94
|
+
* flow. Mining showed stall cues firing while an Agent subagent was
|
|
95
|
+
* mid-dispatch and a research run was committing.
|
|
96
|
+
*
|
|
97
|
+
* `SESSION_STALLED_CUE_WINDOW_MS` — at most one stalled cue per
|
|
98
|
+
* session per hour, total (not per-task). Per-task throttle
|
|
99
|
+
* (`STALLED_FIRE_WINDOW_MS`) remains as a floor on top.
|
|
100
|
+
*/
|
|
101
|
+
const SESSION_ACTIVITY_WINDOW_MS = 5 * 60 * 1000;
|
|
102
|
+
const SESSION_STALLED_CUE_WINDOW_MS = 60 * 60 * 1000;
|
|
103
|
+
/**
|
|
104
|
+
* Walk the transcript tail and return the millisecond age of the most
|
|
105
|
+
* recent `tool_use` entry whose timestamp parses, or `null` when no
|
|
106
|
+
* such entry is found. Best-effort — any read / parse failure returns
|
|
107
|
+
* `null` and the caller falls through to the threshold check.
|
|
108
|
+
*
|
|
109
|
+
* Claude Code transcript entries are JSONL; assistant messages with
|
|
110
|
+
* tool calls carry `type: "tool_use"` on a content block. We scan
|
|
111
|
+
* backwards from the tail and stop at the first matching line.
|
|
112
|
+
*/
|
|
113
|
+
function lastToolUseAgeMs(transcriptPath, nowMs) {
|
|
114
|
+
if (transcriptPath === null || transcriptPath.length === 0)
|
|
115
|
+
return null;
|
|
116
|
+
if (!existsSync(transcriptPath))
|
|
117
|
+
return null;
|
|
118
|
+
let raw;
|
|
119
|
+
try {
|
|
120
|
+
raw = readFileSync(transcriptPath, "utf8");
|
|
121
|
+
}
|
|
122
|
+
catch {
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
const lines = raw.split("\n");
|
|
126
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
127
|
+
const line = lines[i];
|
|
128
|
+
if (line === undefined || line.length === 0)
|
|
129
|
+
continue;
|
|
130
|
+
if (!line.includes('"tool_use"'))
|
|
131
|
+
continue;
|
|
132
|
+
let entry;
|
|
133
|
+
try {
|
|
134
|
+
entry = JSON.parse(line);
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (entry === null || typeof entry !== "object")
|
|
140
|
+
continue;
|
|
141
|
+
const ts = entry["timestamp"]
|
|
142
|
+
?? entry["ts"];
|
|
143
|
+
if (typeof ts !== "string")
|
|
144
|
+
continue;
|
|
145
|
+
const parsed = Date.parse(ts);
|
|
146
|
+
if (Number.isNaN(parsed))
|
|
147
|
+
continue;
|
|
148
|
+
return nowMs - parsed;
|
|
149
|
+
}
|
|
150
|
+
return null;
|
|
151
|
+
}
|
|
152
|
+
function lastSessionStalledCuePath(repoRoot, sessionId) {
|
|
153
|
+
return join(repoRoot, ".cairn", "sessions", sessionId, "last-stalled-cue.iso");
|
|
154
|
+
}
|
|
155
|
+
function lastSessionStalledCueMs(repoRoot, sessionId) {
|
|
156
|
+
const path = lastSessionStalledCuePath(repoRoot, sessionId);
|
|
157
|
+
if (!existsSync(path))
|
|
158
|
+
return null;
|
|
159
|
+
try {
|
|
160
|
+
const raw = readFileSync(path, "utf8").trim();
|
|
161
|
+
const ms = Date.parse(raw);
|
|
162
|
+
return Number.isNaN(ms) ? null : ms;
|
|
163
|
+
}
|
|
164
|
+
catch {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
function stampSessionStalledCue(repoRoot, sessionId) {
|
|
169
|
+
const path = lastSessionStalledCuePath(repoRoot, sessionId);
|
|
170
|
+
try {
|
|
171
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
172
|
+
writeFileSync(path, new Date().toISOString(), "utf8");
|
|
173
|
+
}
|
|
174
|
+
catch {
|
|
175
|
+
// best-effort
|
|
176
|
+
}
|
|
177
|
+
}
|
|
88
178
|
function priorCuePath(repoRoot, sessionId) {
|
|
89
179
|
return join(repoRoot, ".cairn", "sessions", sessionId, "last-stop-cue.json");
|
|
90
180
|
}
|
|
@@ -121,6 +211,7 @@ export async function runStopHook() {
|
|
|
121
211
|
const payload = parseHookPayload(raw);
|
|
122
212
|
const sessionId = typeof payload.session_id === "string" ? payload.session_id : null;
|
|
123
213
|
const cwdInput = typeof payload.cwd === "string" ? payload.cwd : process.cwd();
|
|
214
|
+
const transcriptPath = typeof payload.transcript_path === "string" ? payload.transcript_path : null;
|
|
124
215
|
const repoRoot = resolveRepoRoot(cwdInput);
|
|
125
216
|
const warnings = [];
|
|
126
217
|
let drained = [];
|
|
@@ -176,9 +267,6 @@ export async function runStopHook() {
|
|
|
176
267
|
const hint = `## Cairn — ${grad.completed.length} ${noun} graduated\n\n✓ ${ids} → done. Final attestation written.`;
|
|
177
268
|
reason = reason.length > 0 ? `${reason}\n\n${hint}` : hint;
|
|
178
269
|
}
|
|
179
|
-
if (grad.transitioned.length > 0) {
|
|
180
|
-
warnings.push(`auto_graduated_review_ready:${grad.transitioned.length}`);
|
|
181
|
-
}
|
|
182
270
|
}
|
|
183
271
|
catch (err) {
|
|
184
272
|
warnings.push(`auto_graduate_failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -204,45 +292,73 @@ export async function runStopHook() {
|
|
|
204
292
|
warnings.push(`pending_review_scan_failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
205
293
|
}
|
|
206
294
|
// Stalled-task scanner — surfaces tasks stuck in phase=running
|
|
207
|
-
// with no attestation for
|
|
208
|
-
//
|
|
209
|
-
//
|
|
210
|
-
//
|
|
211
|
-
//
|
|
212
|
-
//
|
|
213
|
-
//
|
|
295
|
+
// with no attestation for 2h+ (raised from 30 min in bug-mine
|
|
296
|
+
// 0.13.8 / Phase 5 — see CONTEXT.md §2.4 for false-fire mining).
|
|
297
|
+
// Catches the failure mode where the autonomous flow finished
|
|
298
|
+
// the work but skipped spawning the reviewer subagent (no
|
|
299
|
+
// attestation → auto-graduator never fires → task accumulates
|
|
300
|
+
// as orphaned). Only fires when no other higher-priority surface
|
|
301
|
+
// (reviewer hint, ctx threshold) already owns the reason channel
|
|
302
|
+
// — stalled-task triage is informational catch-up, not blocking.
|
|
303
|
+
//
|
|
304
|
+
// Three additional gates layered on top of the per-task window:
|
|
214
305
|
//
|
|
215
|
-
//
|
|
216
|
-
//
|
|
217
|
-
//
|
|
218
|
-
//
|
|
306
|
+
// 1. Session-activity gate — when the transcript carries a
|
|
307
|
+
// `tool_use` event within the last 5 minutes, the AI is
|
|
308
|
+
// actively working; suppress entirely.
|
|
309
|
+
// 2. Per-session rate limit — at most one stalled cue per
|
|
310
|
+
// session per hour, total (not per-task). Bug-mine showed
|
|
311
|
+
// three active tasks idle = three prompts per hour without
|
|
312
|
+
// a global cap.
|
|
313
|
+
// 3. Per-task throttle — 60 minute suppression window per
|
|
314
|
+
// task id so the operator isn't asked the same triage
|
|
315
|
+
// question every Stop tick (bug-mine report #9 — same
|
|
316
|
+
// task flagged 3× in 90s).
|
|
219
317
|
if (reason.length === 0 && !isFirstTurnWarmup) {
|
|
220
318
|
try {
|
|
221
319
|
gcStalledWarnedMarkers(repoRoot);
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
warnings.push(`stalled_suppressed_until:${reviewDefer.deferred_at}`);
|
|
320
|
+
const lastSessionCue = sessionId !== null
|
|
321
|
+
? lastSessionStalledCueMs(repoRoot, sessionId)
|
|
322
|
+
: null;
|
|
323
|
+
if (lastSessionCue !== null &&
|
|
324
|
+
Date.now() - lastSessionCue < SESSION_STALLED_CUE_WINDOW_MS) {
|
|
325
|
+
warnings.push(`stalled_session_rate_limited:${new Date(lastSessionCue).toISOString()}`);
|
|
326
|
+
}
|
|
327
|
+
else {
|
|
328
|
+
const recentToolUseAge = lastToolUseAgeMs(transcriptPath, Date.now());
|
|
329
|
+
const sessionActive = recentToolUseAge !== null && recentToolUseAge < SESSION_ACTIVITY_WINDOW_MS;
|
|
330
|
+
if (sessionActive) {
|
|
331
|
+
warnings.push(`stalled_session_active:${recentToolUseAge}`);
|
|
235
332
|
}
|
|
236
333
|
else {
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
334
|
+
const stalled = scanStalledRunningTasks(repoRoot, Date.now(), {
|
|
335
|
+
currentSessionId: sessionId,
|
|
336
|
+
});
|
|
337
|
+
const surfaced = stalled.filter((t) => !isStalledFireSuppressed(repoRoot, t.task_id));
|
|
338
|
+
if (surfaced.length > 0) {
|
|
339
|
+
const reviewDefer = readDeferState(repoRoot, "review");
|
|
340
|
+
const suppressed = reviewDefer !== null &&
|
|
341
|
+
isDeferActive(reviewDefer, new Date(), {
|
|
342
|
+
kind: "task_ids",
|
|
343
|
+
values: surfaced.map((t) => t.task_id),
|
|
344
|
+
});
|
|
345
|
+
if (suppressed) {
|
|
346
|
+
warnings.push(`stalled_suppressed_until:${reviewDefer.deferred_at}`);
|
|
347
|
+
}
|
|
348
|
+
else {
|
|
349
|
+
reason = renderStalledTasksHint(surfaced);
|
|
350
|
+
for (const t of surfaced)
|
|
351
|
+
stampStalledFire(repoRoot, t.task_id);
|
|
352
|
+
if (sessionId !== null)
|
|
353
|
+
stampSessionStalledCue(repoRoot, sessionId);
|
|
354
|
+
warnings.push(`stalled_running_tasks:${surfaced.length}`);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
else if (stalled.length > 0) {
|
|
358
|
+
warnings.push(`stalled_window_suppressed:${stalled.length}`);
|
|
359
|
+
}
|
|
241
360
|
}
|
|
242
361
|
}
|
|
243
|
-
else if (stalled.length > 0) {
|
|
244
|
-
warnings.push(`stalled_window_suppressed:${stalled.length}`);
|
|
245
|
-
}
|
|
246
362
|
}
|
|
247
363
|
catch (err) {
|
|
248
364
|
warnings.push(`stalled_scan_failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -455,22 +571,6 @@ function readTaskPhase(taskDir) {
|
|
|
455
571
|
}
|
|
456
572
|
return null;
|
|
457
573
|
}
|
|
458
|
-
function checkNeedsReview(specPath) {
|
|
459
|
-
try {
|
|
460
|
-
const raw = readFileSync(specPath, "utf8");
|
|
461
|
-
const fmMatch = raw.match(/^---\r?\n([\s\S]*?)\n---/);
|
|
462
|
-
if (!fmMatch)
|
|
463
|
-
return true;
|
|
464
|
-
const fm = fmMatch[1] ?? "";
|
|
465
|
-
const m = fm.match(/^needs_review:\s*(true|false)/m);
|
|
466
|
-
if (m && m[1] === "false")
|
|
467
|
-
return false;
|
|
468
|
-
return true;
|
|
469
|
-
}
|
|
470
|
-
catch {
|
|
471
|
-
return true;
|
|
472
|
-
}
|
|
473
|
-
}
|
|
474
574
|
function scanPendingReviews(repoRoot) {
|
|
475
575
|
const activeDir = join(repoRoot, ".cairn", "tasks", "active");
|
|
476
576
|
if (!existsSync(activeDir))
|
|
@@ -495,10 +595,10 @@ function scanPendingReviews(repoRoot) {
|
|
|
495
595
|
const attestation = join(taskDir, "attestation.yaml");
|
|
496
596
|
if (existsSync(attestation))
|
|
497
597
|
continue;
|
|
498
|
-
// Finding 4: Opt-in reviewer. Default to true, skip if explicitly false.
|
|
499
|
-
if (!checkNeedsReview(tightenedSpec))
|
|
500
|
-
continue;
|
|
501
598
|
// Phase gate — `running` / `tightening` / etc. are not review-ready.
|
|
599
|
+
// Reviewer is now opt-in (bug-mine 0.13.5); this surface only fires
|
|
600
|
+
// when an explicit reviewer subagent set phase=ready_for_review and
|
|
601
|
+
// ended its turn before writing attestation.yaml.
|
|
502
602
|
const phase = readTaskPhase(taskDir);
|
|
503
603
|
if (phase !== null && !REVIEW_READY_PHASES.has(phase))
|
|
504
604
|
continue;
|
|
@@ -591,7 +691,7 @@ function scanStalledRunningTasks(repoRoot, nowMs = Date.now(), opts = { currentS
|
|
|
591
691
|
if (!existsSync(activeDir))
|
|
592
692
|
return [];
|
|
593
693
|
const out = [];
|
|
594
|
-
const idleThresholdMs =
|
|
694
|
+
const idleThresholdMs = 2 * 60 * 60 * 1000;
|
|
595
695
|
const upperBoundMs = 7 * 24 * 60 * 60 * 1000;
|
|
596
696
|
// When a task's last journal write came from a DIFFERENT live session
|
|
597
697
|
// within this window, treat it as "owned by that session" and don't
|
|
@@ -727,9 +827,9 @@ function renderStalledTasksHint(stalled) {
|
|
|
727
827
|
const lines = [
|
|
728
828
|
`## Cairn — ${stalled.length} stalled ${noun}`,
|
|
729
829
|
``,
|
|
730
|
-
`${stalled.length} active ${noun} idle
|
|
830
|
+
`${stalled.length} active ${noun} idle 2h+ with no attestation. ` +
|
|
731
831
|
`Either the autonomous flow skipped the reviewer-spawn step, or the ` +
|
|
732
|
-
`session was interrupted mid-task
|
|
832
|
+
`session was interrupted mid-task.`,
|
|
733
833
|
``,
|
|
734
834
|
];
|
|
735
835
|
for (const t of stalled) {
|
|
@@ -737,7 +837,7 @@ function renderStalledTasksHint(stalled) {
|
|
|
737
837
|
lines.push(`- \`${t.task_id}\` — ${t.title}${mod} (idle ${t.idle_minutes}m)`);
|
|
738
838
|
}
|
|
739
839
|
lines.push("");
|
|
740
|
-
lines.push("
|
|
840
|
+
lines.push("If you reach a stopping point with no obvious continuation, surface to operator (e.g. via `AskUserQuestion`):");
|
|
741
841
|
lines.push("");
|
|
742
842
|
lines.push(`> ${stalled.length} stalled ${noun}. Pick once for all (or address one at a time after):`);
|
|
743
843
|
lines.push(`>`);
|
|
@@ -747,26 +847,30 @@ function renderStalledTasksHint(stalled) {
|
|
|
747
847
|
lines.push(``);
|
|
748
848
|
lines.push("On [a], call `cairn_task_complete({task_id, outcome: \"succeeded\", summary: \"closing stalled task — work landed via prior session\"})` for each id above.");
|
|
749
849
|
lines.push("On [b], dispatch the `reviewer` subagent for each task in turn (one task brief per Task call).");
|
|
750
|
-
lines.push("On [c], end the turn — the prompt re-fires only when status.yaml stays idle past the next
|
|
850
|
+
lines.push("On [c], end the turn — the prompt re-fires only when status.yaml stays idle past the next 2h mark.");
|
|
851
|
+
lines.push("");
|
|
852
|
+
lines.push("If you're actively working (Agent dispatch in flight, edits queued), ignore this hint and keep going — it will re-evaluate on the next Stop tick.");
|
|
751
853
|
return lines.join("\n");
|
|
752
854
|
}
|
|
753
855
|
/**
|
|
754
856
|
* Auto-graduate active tasks based on attestation presence.
|
|
755
857
|
*
|
|
858
|
+
* Self-attest is the default path (bug-mine 0.13.5): the AI calls
|
|
859
|
+
* `cairn_task_complete({outcome, summary})` and the tool moves the
|
|
860
|
+
* directory itself. This auto-graduator is the fallback for the rare
|
|
861
|
+
* case where an opt-in reviewer subagent wrote attestation.yaml but
|
|
862
|
+
* ended its turn before the explicit close call.
|
|
863
|
+
*
|
|
756
864
|
* Rules (only acts on tasks with phase=running):
|
|
757
|
-
* 1. Task-root `attestation.yaml` exists
|
|
758
|
-
*
|
|
759
|
-
* 2. ≥1 subagents/<id>/attestation.yaml AND needs_review=false → succeeded → tasks/done/
|
|
760
|
-
* (trivial task, no reviewer scheduled)
|
|
761
|
-
* 3. ≥1 subagents/<id>/attestation.yaml AND needs_review=true → ready_for_review
|
|
762
|
-
* (reviewer hasn't run yet — `scanPendingReviews` will surface a hint)
|
|
865
|
+
* 1. Task-root `attestation.yaml` exists → succeeded → tasks/done/
|
|
866
|
+
* 2. ≥1 subagents/<id>/attestation.yaml → succeeded → tasks/done/
|
|
763
867
|
*
|
|
764
868
|
* Tasks with no attestation activity stay `running` — they're either
|
|
765
|
-
* still in flight or stalled (
|
|
869
|
+
* still in flight or stalled (stall detection runs separately).
|
|
766
870
|
*/
|
|
767
871
|
function autoGraduateTasks(repoRoot) {
|
|
768
872
|
const activeDir = join(repoRoot, ".cairn", "tasks", "active");
|
|
769
|
-
const result = { completed: []
|
|
873
|
+
const result = { completed: [] };
|
|
770
874
|
if (!existsSync(activeDir))
|
|
771
875
|
return result;
|
|
772
876
|
let entries;
|
|
@@ -785,7 +889,7 @@ function autoGraduateTasks(repoRoot) {
|
|
|
785
889
|
continue;
|
|
786
890
|
if (state.phase !== "running")
|
|
787
891
|
continue;
|
|
788
|
-
if (state.rootAttestation) {
|
|
892
|
+
if (state.rootAttestation || state.subagentAttestations > 0) {
|
|
789
893
|
const r = completeTask({
|
|
790
894
|
repoRoot,
|
|
791
895
|
taskId,
|
|
@@ -794,27 +898,6 @@ function autoGraduateTasks(repoRoot) {
|
|
|
794
898
|
});
|
|
795
899
|
if (r.ok)
|
|
796
900
|
result.completed.push(taskId);
|
|
797
|
-
continue;
|
|
798
|
-
}
|
|
799
|
-
if (state.subagentAttestations > 0) {
|
|
800
|
-
if (!state.needsReview) {
|
|
801
|
-
const r = completeTask({
|
|
802
|
-
repoRoot,
|
|
803
|
-
taskId,
|
|
804
|
-
outcome: "succeeded",
|
|
805
|
-
source: "cairn_stop_auto_graduate",
|
|
806
|
-
});
|
|
807
|
-
if (r.ok)
|
|
808
|
-
result.completed.push(taskId);
|
|
809
|
-
continue;
|
|
810
|
-
}
|
|
811
|
-
const ok = transitionTaskPhase({
|
|
812
|
-
repoRoot,
|
|
813
|
-
taskId,
|
|
814
|
-
newPhase: "ready_for_review",
|
|
815
|
-
});
|
|
816
|
-
if (ok)
|
|
817
|
-
result.transitioned.push(taskId);
|
|
818
901
|
}
|
|
819
902
|
}
|
|
820
903
|
return result;
|