gsd-pi 2.18.0 → 2.19.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/resources/extensions/gsd/auto-dashboard.ts +14 -2
- package/dist/resources/extensions/gsd/auto-prompts.ts +45 -15
- package/dist/resources/extensions/gsd/auto.ts +276 -19
- package/dist/resources/extensions/gsd/captures.ts +384 -0
- package/dist/resources/extensions/gsd/commands.ts +139 -3
- package/dist/resources/extensions/gsd/complexity-classifier.ts +322 -0
- package/dist/resources/extensions/gsd/dashboard-overlay.ts +10 -0
- package/dist/resources/extensions/gsd/metrics.ts +48 -0
- package/dist/resources/extensions/gsd/model-cost-table.ts +65 -0
- package/dist/resources/extensions/gsd/model-router.ts +256 -0
- package/dist/resources/extensions/gsd/post-unit-hooks.ts +2 -1
- package/dist/resources/extensions/gsd/preferences.ts +73 -0
- package/dist/resources/extensions/gsd/prompt-loader.ts +45 -9
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -0
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +8 -0
- package/dist/resources/extensions/gsd/prompts/triage-captures.md +62 -0
- package/dist/resources/extensions/gsd/tests/captures.test.ts +438 -0
- package/dist/resources/extensions/gsd/tests/complexity-classifier.test.ts +181 -0
- package/dist/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +434 -0
- package/dist/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +144 -0
- package/dist/resources/extensions/gsd/tests/model-cost-table.test.ts +69 -0
- package/dist/resources/extensions/gsd/tests/model-router.test.ts +167 -0
- package/dist/resources/extensions/gsd/tests/remote-questions.test.ts +227 -1
- package/dist/resources/extensions/gsd/tests/routing-history.test.ts +215 -62
- package/dist/resources/extensions/gsd/tests/triage-dispatch.test.ts +224 -0
- package/dist/resources/extensions/gsd/tests/triage-resolution.test.ts +215 -0
- package/dist/resources/extensions/gsd/tests/visualizer-data.test.ts +198 -0
- package/dist/resources/extensions/gsd/tests/visualizer-views.test.ts +255 -0
- package/dist/resources/extensions/gsd/triage-resolution.ts +200 -0
- package/dist/resources/extensions/gsd/triage-ui.ts +175 -0
- package/dist/resources/extensions/gsd/visualizer-data.ts +154 -0
- package/dist/resources/extensions/gsd/visualizer-overlay.ts +193 -0
- package/dist/resources/extensions/gsd/visualizer-views.ts +293 -0
- package/dist/resources/extensions/remote-questions/discord-adapter.ts +33 -0
- package/dist/resources/extensions/remote-questions/format.ts +12 -6
- package/dist/resources/extensions/remote-questions/manager.ts +8 -0
- package/package.json +1 -1
- package/src/resources/extensions/gsd/auto-dashboard.ts +14 -2
- package/src/resources/extensions/gsd/auto-prompts.ts +45 -15
- package/src/resources/extensions/gsd/auto.ts +276 -19
- package/src/resources/extensions/gsd/captures.ts +384 -0
- package/src/resources/extensions/gsd/commands.ts +139 -3
- package/src/resources/extensions/gsd/complexity-classifier.ts +322 -0
- package/src/resources/extensions/gsd/dashboard-overlay.ts +10 -0
- package/src/resources/extensions/gsd/metrics.ts +48 -0
- package/src/resources/extensions/gsd/model-cost-table.ts +65 -0
- package/src/resources/extensions/gsd/model-router.ts +256 -0
- package/src/resources/extensions/gsd/post-unit-hooks.ts +2 -1
- package/src/resources/extensions/gsd/preferences.ts +73 -0
- package/src/resources/extensions/gsd/prompt-loader.ts +45 -9
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +6 -0
- package/src/resources/extensions/gsd/prompts/replan-slice.md +8 -0
- package/src/resources/extensions/gsd/prompts/triage-captures.md +62 -0
- package/src/resources/extensions/gsd/tests/captures.test.ts +438 -0
- package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +181 -0
- package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +434 -0
- package/src/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +144 -0
- package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +69 -0
- package/src/resources/extensions/gsd/tests/model-router.test.ts +167 -0
- package/src/resources/extensions/gsd/tests/remote-questions.test.ts +227 -1
- package/src/resources/extensions/gsd/tests/routing-history.test.ts +215 -62
- package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +224 -0
- package/src/resources/extensions/gsd/tests/triage-resolution.test.ts +215 -0
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +198 -0
- package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +255 -0
- package/src/resources/extensions/gsd/triage-resolution.ts +200 -0
- package/src/resources/extensions/gsd/triage-ui.ts +175 -0
- package/src/resources/extensions/gsd/visualizer-data.ts +154 -0
- package/src/resources/extensions/gsd/visualizer-overlay.ts +193 -0
- package/src/resources/extensions/gsd/visualizer-views.ts +293 -0
- package/src/resources/extensions/remote-questions/discord-adapter.ts +33 -0
- package/src/resources/extensions/remote-questions/format.ts +12 -6
- package/src/resources/extensions/remote-questions/manager.ts +8 -0
|
@@ -1,87 +1,240 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Routing History — structural tests for adaptive learning module.
|
|
3
|
-
*
|
|
4
|
-
* Verifies routing-history.ts exports and structure from #579.
|
|
5
|
-
* Uses source-level checks to avoid @gsd/pi-coding-agent import chain.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
1
|
import test from "node:test";
|
|
9
2
|
import assert from "node:assert/strict";
|
|
10
|
-
import { readFileSync } from "node:fs";
|
|
11
|
-
import { join
|
|
12
|
-
import {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
3
|
+
import { mkdirSync, rmSync, writeFileSync, readFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
|
|
7
|
+
import {
|
|
8
|
+
initRoutingHistory,
|
|
9
|
+
resetRoutingHistory,
|
|
10
|
+
recordOutcome,
|
|
11
|
+
recordFeedback,
|
|
12
|
+
getAdaptiveTierAdjustment,
|
|
13
|
+
clearRoutingHistory,
|
|
14
|
+
getRoutingHistory,
|
|
15
|
+
} from "../routing-history.js";
|
|
16
|
+
|
|
17
|
+
// ─── Test Setup ──────────────────────────────────────────────────────────────
|
|
18
|
+
|
|
19
|
+
function makeTmpDir(): string {
|
|
20
|
+
const dir = join(tmpdir(), `gsd-routing-test-${Date.now()}-${Math.random().toString(36).slice(2)}`);
|
|
21
|
+
mkdirSync(join(dir, ".gsd"), { recursive: true });
|
|
22
|
+
return dir;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function cleanup(dir: string): void {
|
|
26
|
+
try { rmSync(dir, { recursive: true, force: true }); } catch {}
|
|
27
|
+
resetRoutingHistory();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// ─── recordOutcome ───────────────────────────────────────────────────────────
|
|
31
|
+
|
|
32
|
+
test("recordOutcome tracks success and failure counts", () => {
|
|
33
|
+
const dir = makeTmpDir();
|
|
34
|
+
try {
|
|
35
|
+
initRoutingHistory(dir);
|
|
36
|
+
recordOutcome("execute-task", "standard", true);
|
|
37
|
+
recordOutcome("execute-task", "standard", true);
|
|
38
|
+
recordOutcome("execute-task", "standard", false);
|
|
39
|
+
|
|
40
|
+
const history = getRoutingHistory();
|
|
41
|
+
assert.ok(history);
|
|
42
|
+
const pattern = history.patterns["execute-task"];
|
|
43
|
+
assert.ok(pattern);
|
|
44
|
+
assert.equal(pattern.standard.success, 2);
|
|
45
|
+
assert.equal(pattern.standard.fail, 1);
|
|
46
|
+
} finally {
|
|
47
|
+
cleanup(dir);
|
|
48
|
+
}
|
|
23
49
|
});
|
|
24
50
|
|
|
25
|
-
test("
|
|
26
|
-
|
|
51
|
+
test("recordOutcome tracks tag-specific patterns", () => {
|
|
52
|
+
const dir = makeTmpDir();
|
|
53
|
+
try {
|
|
54
|
+
initRoutingHistory(dir);
|
|
55
|
+
recordOutcome("execute-task", "light", true, ["docs"]);
|
|
56
|
+
|
|
57
|
+
const history = getRoutingHistory();
|
|
58
|
+
assert.ok(history);
|
|
59
|
+
assert.ok(history.patterns["execute-task:docs"]);
|
|
60
|
+
assert.equal(history.patterns["execute-task:docs"].light.success, 1);
|
|
61
|
+
} finally {
|
|
62
|
+
cleanup(dir);
|
|
63
|
+
}
|
|
27
64
|
});
|
|
28
65
|
|
|
29
|
-
test("
|
|
30
|
-
|
|
66
|
+
test("recordOutcome applies rolling window", () => {
|
|
67
|
+
const dir = makeTmpDir();
|
|
68
|
+
try {
|
|
69
|
+
initRoutingHistory(dir);
|
|
70
|
+
// Record 60 successes — should be capped to 50
|
|
71
|
+
for (let i = 0; i < 60; i++) {
|
|
72
|
+
recordOutcome("execute-task", "standard", true);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const history = getRoutingHistory();
|
|
76
|
+
assert.ok(history);
|
|
77
|
+
const total = history.patterns["execute-task"].standard.success +
|
|
78
|
+
history.patterns["execute-task"].standard.fail;
|
|
79
|
+
assert.ok(total <= 50, `total ${total} should be <= 50`);
|
|
80
|
+
} finally {
|
|
81
|
+
cleanup(dir);
|
|
82
|
+
}
|
|
31
83
|
});
|
|
32
84
|
|
|
33
|
-
|
|
34
|
-
|
|
85
|
+
// ─── getAdaptiveTierAdjustment ───────────────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
test("no adjustment when insufficient data", () => {
|
|
88
|
+
const dir = makeTmpDir();
|
|
89
|
+
try {
|
|
90
|
+
initRoutingHistory(dir);
|
|
91
|
+
recordOutcome("execute-task", "light", false);
|
|
92
|
+
// Only 1 data point — not enough
|
|
93
|
+
const adj = getAdaptiveTierAdjustment("execute-task", "light");
|
|
94
|
+
assert.equal(adj, null);
|
|
95
|
+
} finally {
|
|
96
|
+
cleanup(dir);
|
|
97
|
+
}
|
|
35
98
|
});
|
|
36
99
|
|
|
37
|
-
test("
|
|
38
|
-
|
|
100
|
+
test("bumps tier when failure rate exceeds threshold", () => {
|
|
101
|
+
const dir = makeTmpDir();
|
|
102
|
+
try {
|
|
103
|
+
initRoutingHistory(dir);
|
|
104
|
+
// Record high failure rate at light tier
|
|
105
|
+
recordOutcome("execute-task", "light", false);
|
|
106
|
+
recordOutcome("execute-task", "light", false);
|
|
107
|
+
recordOutcome("execute-task", "light", true);
|
|
108
|
+
// 2/3 = 66% failure rate > 20% threshold
|
|
109
|
+
|
|
110
|
+
const adj = getAdaptiveTierAdjustment("execute-task", "light");
|
|
111
|
+
assert.equal(adj, "standard");
|
|
112
|
+
} finally {
|
|
113
|
+
cleanup(dir);
|
|
114
|
+
}
|
|
39
115
|
});
|
|
40
116
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
117
|
+
test("no adjustment when success rate is high", () => {
|
|
118
|
+
const dir = makeTmpDir();
|
|
119
|
+
try {
|
|
120
|
+
initRoutingHistory(dir);
|
|
121
|
+
for (let i = 0; i < 10; i++) {
|
|
122
|
+
recordOutcome("execute-task", "light", true);
|
|
123
|
+
}
|
|
124
|
+
const adj = getAdaptiveTierAdjustment("execute-task", "light");
|
|
125
|
+
assert.equal(adj, null);
|
|
126
|
+
} finally {
|
|
127
|
+
cleanup(dir);
|
|
128
|
+
}
|
|
47
129
|
});
|
|
48
130
|
|
|
49
|
-
test("
|
|
50
|
-
|
|
131
|
+
test("tag-specific patterns take precedence", () => {
|
|
132
|
+
const dir = makeTmpDir();
|
|
133
|
+
try {
|
|
134
|
+
initRoutingHistory(dir);
|
|
135
|
+
// Base pattern has high success rate (tagged calls also count toward base)
|
|
136
|
+
for (let i = 0; i < 15; i++) {
|
|
137
|
+
recordOutcome("execute-task", "light", true);
|
|
138
|
+
}
|
|
139
|
+
// But docs-tagged tasks fail at light
|
|
140
|
+
recordOutcome("execute-task", "light", false, ["docs"]);
|
|
141
|
+
recordOutcome("execute-task", "light", false, ["docs"]);
|
|
142
|
+
recordOutcome("execute-task", "light", true, ["docs"]);
|
|
143
|
+
|
|
144
|
+
// With tags, should bump (docs pattern: 1/3 success = 66% failure)
|
|
145
|
+
const adj = getAdaptiveTierAdjustment("execute-task", "light", ["docs"]);
|
|
146
|
+
assert.equal(adj, "standard");
|
|
147
|
+
|
|
148
|
+
// Without tags, should not bump (base: 16/18 success = 11% failure)
|
|
149
|
+
const adjBase = getAdaptiveTierAdjustment("execute-task", "light");
|
|
150
|
+
assert.equal(adjBase, null);
|
|
151
|
+
} finally {
|
|
152
|
+
cleanup(dir);
|
|
153
|
+
}
|
|
51
154
|
});
|
|
52
155
|
|
|
53
|
-
|
|
54
|
-
|
|
156
|
+
// ─── recordFeedback ──────────────────────────────────────────────────────────
|
|
157
|
+
|
|
158
|
+
test("recordFeedback stores feedback entries", () => {
|
|
159
|
+
const dir = makeTmpDir();
|
|
160
|
+
try {
|
|
161
|
+
initRoutingHistory(dir);
|
|
162
|
+
recordFeedback("execute-task", "M001/S01/T01", "standard", "over");
|
|
163
|
+
|
|
164
|
+
const history = getRoutingHistory();
|
|
165
|
+
assert.ok(history);
|
|
166
|
+
assert.equal(history.feedback.length, 1);
|
|
167
|
+
assert.equal(history.feedback[0].rating, "over");
|
|
168
|
+
assert.equal(history.feedback[0].tier, "standard");
|
|
169
|
+
} finally {
|
|
170
|
+
cleanup(dir);
|
|
171
|
+
}
|
|
55
172
|
});
|
|
56
173
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
174
|
+
test("recordFeedback 'under' increases failure count at tier", () => {
|
|
175
|
+
const dir = makeTmpDir();
|
|
176
|
+
try {
|
|
177
|
+
initRoutingHistory(dir);
|
|
178
|
+
recordFeedback("execute-task", "M001/S01/T01", "light", "under");
|
|
179
|
+
|
|
180
|
+
const history = getRoutingHistory();
|
|
181
|
+
assert.ok(history);
|
|
182
|
+
// "under" adds 2 (FEEDBACK_WEIGHT) failures
|
|
183
|
+
assert.equal(history.patterns["execute-task"].light.fail, 2);
|
|
184
|
+
} finally {
|
|
185
|
+
cleanup(dir);
|
|
186
|
+
}
|
|
70
187
|
});
|
|
71
188
|
|
|
72
|
-
test("
|
|
73
|
-
|
|
189
|
+
test("recordFeedback 'over' increases success count at lower tier", () => {
|
|
190
|
+
const dir = makeTmpDir();
|
|
191
|
+
try {
|
|
192
|
+
initRoutingHistory(dir);
|
|
193
|
+
recordFeedback("execute-task", "M001/S01/T01", "standard", "over");
|
|
194
|
+
|
|
195
|
+
const history = getRoutingHistory();
|
|
196
|
+
assert.ok(history);
|
|
197
|
+
// "over" at standard → adds 2 successes at light
|
|
198
|
+
assert.equal(history.patterns["execute-task"].light.success, 2);
|
|
199
|
+
} finally {
|
|
200
|
+
cleanup(dir);
|
|
201
|
+
}
|
|
74
202
|
});
|
|
75
203
|
|
|
76
|
-
//
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
204
|
+
// ─── clearRoutingHistory ─────────────────────────────────────────────────────
|
|
205
|
+
|
|
206
|
+
test("clearRoutingHistory resets all data", () => {
|
|
207
|
+
const dir = makeTmpDir();
|
|
208
|
+
try {
|
|
209
|
+
initRoutingHistory(dir);
|
|
210
|
+
recordOutcome("execute-task", "light", true);
|
|
211
|
+
clearRoutingHistory(dir);
|
|
212
|
+
|
|
213
|
+
const history = getRoutingHistory();
|
|
214
|
+
assert.ok(history);
|
|
215
|
+
assert.deepEqual(history.patterns, {});
|
|
216
|
+
assert.deepEqual(history.feedback, []);
|
|
217
|
+
} finally {
|
|
218
|
+
cleanup(dir);
|
|
219
|
+
}
|
|
82
220
|
});
|
|
83
221
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
222
|
+
// ─── Persistence ─────────────────────────────────────────────────────────────
|
|
223
|
+
|
|
224
|
+
test("routing history persists to disk and reloads", () => {
|
|
225
|
+
const dir = makeTmpDir();
|
|
226
|
+
try {
|
|
227
|
+
initRoutingHistory(dir);
|
|
228
|
+
recordOutcome("execute-task", "standard", true);
|
|
229
|
+
recordOutcome("execute-task", "standard", true);
|
|
230
|
+
resetRoutingHistory();
|
|
231
|
+
|
|
232
|
+
// Reload from disk
|
|
233
|
+
initRoutingHistory(dir);
|
|
234
|
+
const history = getRoutingHistory();
|
|
235
|
+
assert.ok(history);
|
|
236
|
+
assert.equal(history.patterns["execute-task"].standard.success, 2);
|
|
237
|
+
} finally {
|
|
238
|
+
cleanup(dir);
|
|
239
|
+
}
|
|
87
240
|
});
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Triage dispatch ordering contract tests.
|
|
3
|
+
*
|
|
4
|
+
* These tests verify structural invariants of the triage integration
|
|
5
|
+
* by inspecting the actual source code of auto.ts and post-unit-hooks.ts.
|
|
6
|
+
* Full behavioral testing requires the @gsd/pi-coding-agent runtime.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import test from "node:test";
|
|
10
|
+
import assert from "node:assert/strict";
|
|
11
|
+
import { readFileSync } from "node:fs";
|
|
12
|
+
import { join, dirname } from "node:path";
|
|
13
|
+
import { fileURLToPath } from "node:url";
|
|
14
|
+
|
|
15
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
const autoPath = join(__dirname, "..", "auto.ts");
|
|
17
|
+
const hooksPath = join(__dirname, "..", "post-unit-hooks.ts");
|
|
18
|
+
const autoPromptsPath = join(__dirname, "..", "auto-prompts.ts");
|
|
19
|
+
|
|
20
|
+
const autoSrc = readFileSync(autoPath, "utf-8");
|
|
21
|
+
const hooksSrc = readFileSync(hooksPath, "utf-8");
|
|
22
|
+
const autoPromptsSrc = (() => { try { return readFileSync(autoPromptsPath, "utf-8"); } catch { return autoSrc; } })();
|
|
23
|
+
|
|
24
|
+
// ─── Hook exclusion ──────────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
test("dispatch: triage-captures excluded from post-unit hook triggering", () => {
|
|
27
|
+
// post-unit-hooks.ts must return null for triage-captures unit type
|
|
28
|
+
assert.ok(
|
|
29
|
+
hooksSrc.includes('"triage-captures"'),
|
|
30
|
+
"post-unit-hooks.ts should reference triage-captures",
|
|
31
|
+
);
|
|
32
|
+
assert.ok(
|
|
33
|
+
hooksSrc.includes('completedUnitType === "triage-captures"'),
|
|
34
|
+
"should check for triage-captures in the hook exclusion guard",
|
|
35
|
+
);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// ─── Triage check placement ──────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
test("dispatch: triage check appears after hook section and before stepMode check", () => {
|
|
41
|
+
const hookRetryIndex = autoSrc.indexOf("isRetryPending()");
|
|
42
|
+
// Find the triage check in handleAgentEnd (not in getAutoDashboardData)
|
|
43
|
+
const triageCheckIndex = autoSrc.indexOf("Triage check: dispatch triage unit");
|
|
44
|
+
const stepModeIndex = autoSrc.indexOf("In step mode, pause and show a wizard");
|
|
45
|
+
|
|
46
|
+
assert.ok(hookRetryIndex > 0, "hook retry check should exist");
|
|
47
|
+
assert.ok(triageCheckIndex > 0, "triage check block should exist");
|
|
48
|
+
assert.ok(stepModeIndex > 0, "step mode check should exist");
|
|
49
|
+
|
|
50
|
+
assert.ok(
|
|
51
|
+
triageCheckIndex > hookRetryIndex,
|
|
52
|
+
"triage check should come after hook retry check",
|
|
53
|
+
);
|
|
54
|
+
assert.ok(
|
|
55
|
+
triageCheckIndex < stepModeIndex,
|
|
56
|
+
"triage check should come before stepMode check",
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// ─── Guard conditions ────────────────────────────────────────────────────────
|
|
61
|
+
|
|
62
|
+
test("dispatch: triage check guards against step mode", () => {
|
|
63
|
+
// The triage block should check !stepMode
|
|
64
|
+
const triageBlock = autoSrc.slice(
|
|
65
|
+
autoSrc.indexOf("Triage check: dispatch triage unit"),
|
|
66
|
+
autoSrc.indexOf("In step mode, pause and show a wizard"),
|
|
67
|
+
);
|
|
68
|
+
assert.ok(
|
|
69
|
+
triageBlock.includes("!stepMode"),
|
|
70
|
+
"triage block should guard against step mode",
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("dispatch: triage check guards against hook unit types", () => {
|
|
75
|
+
const triageBlock = autoSrc.slice(
|
|
76
|
+
autoSrc.indexOf("Triage check: dispatch triage unit"),
|
|
77
|
+
autoSrc.indexOf("In step mode, pause and show a wizard"),
|
|
78
|
+
);
|
|
79
|
+
assert.ok(
|
|
80
|
+
triageBlock.includes('!currentUnit.type.startsWith("hook/")'),
|
|
81
|
+
"triage block should not fire for hook units",
|
|
82
|
+
);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("dispatch: triage check guards against triage-on-triage", () => {
|
|
86
|
+
const triageBlock = autoSrc.slice(
|
|
87
|
+
autoSrc.indexOf("Triage check: dispatch triage unit"),
|
|
88
|
+
autoSrc.indexOf("In step mode, pause and show a wizard"),
|
|
89
|
+
);
|
|
90
|
+
assert.ok(
|
|
91
|
+
triageBlock.includes('currentUnit.type !== "triage-captures"'),
|
|
92
|
+
"triage block should not fire for triage units",
|
|
93
|
+
);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test("dispatch: triage check guards against quick-task triggering triage", () => {
|
|
97
|
+
const triageBlock = autoSrc.slice(
|
|
98
|
+
autoSrc.indexOf("Triage check: dispatch triage unit"),
|
|
99
|
+
autoSrc.indexOf("In step mode, pause and show a wizard"),
|
|
100
|
+
);
|
|
101
|
+
assert.ok(
|
|
102
|
+
triageBlock.includes('currentUnit.type !== "quick-task"'),
|
|
103
|
+
"triage block should not fire for quick-task units",
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("dispatch: triage dispatch uses early-return pattern", () => {
|
|
108
|
+
const triageBlock = autoSrc.slice(
|
|
109
|
+
autoSrc.indexOf("Triage check: dispatch triage unit"),
|
|
110
|
+
autoSrc.indexOf("In step mode, pause and show a wizard"),
|
|
111
|
+
);
|
|
112
|
+
assert.ok(
|
|
113
|
+
triageBlock.includes("return; // handleAgentEnd will fire again"),
|
|
114
|
+
"triage dispatch should return after sending message",
|
|
115
|
+
);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test("dispatch: triage imports hasPendingCaptures and loadPendingCaptures", () => {
|
|
119
|
+
assert.ok(
|
|
120
|
+
autoSrc.includes('hasPendingCaptures, loadPendingCaptures, countPendingCaptures') &&
|
|
121
|
+
autoSrc.includes('from "./captures.js"'),
|
|
122
|
+
"auto.ts should import capture functions including countPendingCaptures",
|
|
123
|
+
);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
// ─── Prompt integration ──────────────────────────────────────────────────────
|
|
127
|
+
|
|
128
|
+
test("dispatch: replan prompt builder loads capture context", () => {
|
|
129
|
+
const src = autoPromptsSrc;
|
|
130
|
+
assert.ok(
|
|
131
|
+
src.includes("loadReplanCaptures"),
|
|
132
|
+
"buildReplanSlicePrompt should load replan captures",
|
|
133
|
+
);
|
|
134
|
+
assert.ok(
|
|
135
|
+
src.includes("captureContext"),
|
|
136
|
+
"buildReplanSlicePrompt should pass captureContext to template",
|
|
137
|
+
);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
test("dispatch: reassess prompt builder loads deferred captures", () => {
|
|
141
|
+
const src = autoPromptsSrc;
|
|
142
|
+
assert.ok(
|
|
143
|
+
src.includes("loadDeferredCaptures"),
|
|
144
|
+
"buildReassessRoadmapPrompt should load deferred captures",
|
|
145
|
+
);
|
|
146
|
+
assert.ok(
|
|
147
|
+
src.includes("deferredCaptures"),
|
|
148
|
+
"buildReassessRoadmapPrompt should pass deferredCaptures to template",
|
|
149
|
+
);
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
// ─── Prompt templates ────────────────────────────────────────────────────────
|
|
153
|
+
|
|
154
|
+
test("dispatch: replan prompt template includes captureContext variable", () => {
|
|
155
|
+
const promptPath = join(__dirname, "..", "prompts", "replan-slice.md");
|
|
156
|
+
const prompt = readFileSync(promptPath, "utf-8");
|
|
157
|
+
assert.ok(
|
|
158
|
+
prompt.includes("{{captureContext}}"),
|
|
159
|
+
"replan-slice.md should include {{captureContext}}",
|
|
160
|
+
);
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
test("dispatch: reassess prompt template includes deferredCaptures variable", () => {
|
|
164
|
+
const promptPath = join(__dirname, "..", "prompts", "reassess-roadmap.md");
|
|
165
|
+
const prompt = readFileSync(promptPath, "utf-8");
|
|
166
|
+
assert.ok(
|
|
167
|
+
prompt.includes("{{deferredCaptures}}"),
|
|
168
|
+
"reassess-roadmap.md should include {{deferredCaptures}}",
|
|
169
|
+
);
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
test("dispatch: triage prompt template exists and has classification criteria", () => {
|
|
173
|
+
const promptPath = join(__dirname, "..", "prompts", "triage-captures.md");
|
|
174
|
+
const prompt = readFileSync(promptPath, "utf-8");
|
|
175
|
+
assert.ok(prompt.includes("quick-task"), "should have quick-task classification");
|
|
176
|
+
assert.ok(prompt.includes("inject"), "should have inject classification");
|
|
177
|
+
assert.ok(prompt.includes("defer"), "should have defer classification");
|
|
178
|
+
assert.ok(prompt.includes("replan"), "should have replan classification");
|
|
179
|
+
assert.ok(prompt.includes("note"), "should have note classification");
|
|
180
|
+
assert.ok(prompt.includes("{{pendingCaptures}}"), "should have pending captures variable");
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
// ─── Dashboard integration ───────────────────────────────────────────────────
|
|
184
|
+
|
|
185
|
+
test("dashboard: AutoDashboardData includes pendingCaptureCount field", () => {
|
|
186
|
+
assert.ok(
|
|
187
|
+
autoSrc.includes("pendingCaptureCount"),
|
|
188
|
+
"auto.ts should have pendingCaptureCount in AutoDashboardData",
|
|
189
|
+
);
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test("dashboard: getAutoDashboardData computes pendingCaptureCount", () => {
|
|
193
|
+
assert.ok(
|
|
194
|
+
autoSrc.includes("pendingCaptureCount = countPendingCaptures") ||
|
|
195
|
+
autoSrc.includes("pendingCaptureCount = countPendingCaptures(basePath)"),
|
|
196
|
+
"getAutoDashboardData should compute pendingCaptureCount from countPendingCaptures (single-read)",
|
|
197
|
+
);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test("dashboard: overlay renders pending captures badge", () => {
|
|
201
|
+
const overlayPath = join(__dirname, "..", "dashboard-overlay.ts");
|
|
202
|
+
const overlaySrc = readFileSync(overlayPath, "utf-8");
|
|
203
|
+
assert.ok(
|
|
204
|
+
overlaySrc.includes("pendingCaptureCount"),
|
|
205
|
+
"dashboard-overlay.ts should reference pendingCaptureCount",
|
|
206
|
+
);
|
|
207
|
+
assert.ok(
|
|
208
|
+
overlaySrc.includes("pending capture"),
|
|
209
|
+
"dashboard-overlay.ts should show pending captures text",
|
|
210
|
+
);
|
|
211
|
+
});
|
|
212
|
+
|
|
213
|
+
test("dashboard: overlay labels triage-captures and quick-task unit types", () => {
|
|
214
|
+
const overlayPath = join(__dirname, "..", "dashboard-overlay.ts");
|
|
215
|
+
const overlaySrc = readFileSync(overlayPath, "utf-8");
|
|
216
|
+
assert.ok(
|
|
217
|
+
overlaySrc.includes('"triage-captures"'),
|
|
218
|
+
"unitLabel should handle triage-captures",
|
|
219
|
+
);
|
|
220
|
+
assert.ok(
|
|
221
|
+
overlaySrc.includes('"quick-task"'),
|
|
222
|
+
"unitLabel should handle quick-task",
|
|
223
|
+
);
|
|
224
|
+
});
|