gsd-pi 2.44.0-dev.62b5d6c → 2.44.0-dev.848dd4c
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/README.md +30 -12
- package/dist/resources/extensions/gsd/auto-start.js +10 -0
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +5 -0
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +18 -18
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +18 -18
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js +6 -8
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +24 -26
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/fs-utils.test.js +29 -48
- package/packages/pi-coding-agent/dist/core/fs-utils.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js +34 -44
- package/packages/pi-coding-agent/dist/core/resolve-config-value.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.test.js +30 -34
- package/packages/pi-coding-agent/dist/core/session-manager.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js +10 -12
- package/packages/pi-coding-agent/dist/core/tools/edit-diff.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js +43 -47
- package/packages/pi-coding-agent/dist/resources/extensions/memory/storage.test.js.map +1 -1
- package/packages/pi-coding-agent/src/core/auth-storage.test.ts +7 -7
- package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +26 -26
- package/packages/pi-coding-agent/src/core/fs-utils.test.ts +31 -43
- package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +40 -45
- package/packages/pi-coding-agent/src/core/session-manager.test.ts +33 -33
- package/packages/pi-coding-agent/src/core/tools/edit-diff.test.ts +17 -17
- package/packages/pi-coding-agent/src/resources/extensions/memory/storage.test.ts +74 -74
- package/src/resources/extensions/gsd/auto-start.ts +14 -0
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +8 -0
- package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +99 -99
- package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +14 -16
- package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +43 -57
- package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +11 -13
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +465 -523
- package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +73 -75
- package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +34 -56
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +533 -656
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +165 -143
- package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +29 -52
- package/src/resources/extensions/gsd/tests/captures.test.ts +148 -176
- package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +32 -33
- package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +141 -143
- package/src/resources/extensions/gsd/tests/commands-inspect-open-db.test.ts +25 -25
- package/src/resources/extensions/gsd/tests/commands-logs.test.ts +81 -81
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +38 -59
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +228 -263
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +250 -302
- package/src/resources/extensions/gsd/tests/context-store.test.ts +354 -367
- package/src/resources/extensions/gsd/tests/continue-here.test.ts +68 -72
- package/src/resources/extensions/gsd/tests/cost-projection.test.ts +92 -106
- package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +27 -35
- package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +220 -237
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +390 -420
- package/src/resources/extensions/gsd/tests/definition-loader.test.ts +76 -92
- package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +68 -83
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +152 -183
- package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +78 -101
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +192 -227
- package/src/resources/extensions/gsd/tests/detection.test.ts +232 -278
- package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +30 -34
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +164 -180
- package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +43 -49
- package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +28 -32
- package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +27 -29
- package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +34 -38
- package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +54 -75
- package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +21 -32
- package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +72 -97
- package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +38 -44
- package/src/resources/extensions/gsd/tests/doctor-git.test.ts +104 -145
- package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +84 -106
- package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +54 -60
- package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +72 -93
- package/src/resources/extensions/gsd/tests/doctor.test.ts +104 -134
- package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +123 -131
- package/src/resources/extensions/gsd/tests/exit-command.test.ts +20 -24
- package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +48 -57
- package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +5 -7
- package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +30 -42
- package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +198 -206
- package/src/resources/extensions/gsd/tests/git-locale.test.ts +13 -27
- package/src/resources/extensions/gsd/tests/git-service.test.ts +285 -388
- package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +31 -39
- package/src/resources/extensions/gsd/tests/graph-operations.test.ts +63 -69
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +255 -264
- package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +108 -119
- package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +81 -103
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +229 -262
- package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +29 -37
- package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +81 -102
- package/src/resources/extensions/gsd/tests/init-wizard.test.ts +16 -18
- package/src/resources/extensions/gsd/tests/integration-edge.test.ts +41 -46
- package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +42 -53
- package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +75 -91
- package/src/resources/extensions/gsd/tests/integration-proof.test.ts +18 -18
- package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +150 -194
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +101 -125
- package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +45 -54
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +80 -93
- package/src/resources/extensions/gsd/tests/migrate-command.test.ts +57 -66
- package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +83 -93
- package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +161 -170
- package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +125 -141
- package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +107 -131
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +87 -96
- package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +125 -164
- package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +81 -94
- package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +35 -36
- package/src/resources/extensions/gsd/tests/overrides.test.ts +99 -106
- package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +40 -47
- package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +25 -28
- package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +66 -83
- package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +54 -77
- package/src/resources/extensions/gsd/tests/park-milestone.test.ts +68 -115
- package/src/resources/extensions/gsd/tests/parsers.test.ts +546 -611
- package/src/resources/extensions/gsd/tests/paths.test.ts +72 -87
- package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +77 -117
- package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
- package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +93 -119
- package/src/resources/extensions/gsd/tests/queue-order.test.ts +70 -82
- package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +42 -55
- package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +100 -0
- package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +45 -73
- package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +28 -38
- package/src/resources/extensions/gsd/tests/replan-slice.test.ts +73 -80
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +71 -74
- package/src/resources/extensions/gsd/tests/requirements.test.ts +70 -75
- package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +44 -66
- package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +114 -181
- package/src/resources/extensions/gsd/tests/rule-registry.test.ts +63 -65
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +66 -128
- package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +18 -25
- package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +37 -44
- package/src/resources/extensions/gsd/tests/shared-wal.test.ts +19 -26
- package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +6 -8
- package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +22 -28
- package/src/resources/extensions/gsd/tests/token-savings.test.ts +54 -56
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +23 -25
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +9 -11
- package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +66 -82
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +46 -47
- package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +20 -22
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +84 -86
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +41 -43
- package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +94 -96
- package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +11 -13
- package/src/resources/extensions/gsd/tests/worker-registry.test.ts +27 -29
- package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +50 -52
- package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +10 -13
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +14 -18
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +38 -39
- package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +17 -21
- package/src/resources/extensions/gsd/tests/worktree-health.test.ts +25 -30
- package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +30 -37
- package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +15 -22
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +59 -66
- package/src/resources/extensions/gsd/tests/worktree.test.ts +44 -50
- /package/dist/web/standalone/.next/static/{fOnWQBjWXMKUs4bqTg530 → -zps1Q9mQmioAKLcQiCr8}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{fOnWQBjWXMKUs4bqTg530 → -zps1Q9mQmioAKLcQiCr8}/_ssgManifest.js +0 -0
|
@@ -162,7 +162,7 @@ describe("continue-here", () => {
|
|
|
162
162
|
});
|
|
163
163
|
|
|
164
164
|
describe("continueHereFired runtime record field", () => {
|
|
165
|
-
it("AutoUnitRuntimeRecord includes continueHereFired with default false", async () => {
|
|
165
|
+
it("AutoUnitRuntimeRecord includes continueHereFired with default false", async (t) => {
|
|
166
166
|
// Import writeUnitRuntimeRecord to verify the field is present and defaults
|
|
167
167
|
const { writeUnitRuntimeRecord, readUnitRuntimeRecord, clearUnitRuntimeRecord } = await import("../unit-runtime.js");
|
|
168
168
|
const fs = await import("node:fs");
|
|
@@ -171,87 +171,83 @@ describe("continue-here", () => {
|
|
|
171
171
|
|
|
172
172
|
// Use a temp directory as basePath
|
|
173
173
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "continue-here-test-"));
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
201
|
-
}
|
|
174
|
+
t.after(() => fs.rmSync(tmpDir, { recursive: true, force: true }));
|
|
175
|
+
|
|
176
|
+
const record = writeUnitRuntimeRecord(tmpDir, "execute-task", "M007/S02/T02", Date.now(), {
|
|
177
|
+
phase: "dispatched",
|
|
178
|
+
wrapupWarningSent: false,
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
assert.equal(record.continueHereFired, false, "default continueHereFired should be false");
|
|
182
|
+
|
|
183
|
+
// Verify it persists to disk
|
|
184
|
+
const read = readUnitRuntimeRecord(tmpDir, "execute-task", "M007/S02/T02");
|
|
185
|
+
assert.ok(read, "record should be readable");
|
|
186
|
+
assert.equal(read!.continueHereFired, false);
|
|
187
|
+
|
|
188
|
+
// Update to true
|
|
189
|
+
const updated = writeUnitRuntimeRecord(tmpDir, "execute-task", "M007/S02/T02", Date.now(), {
|
|
190
|
+
continueHereFired: true,
|
|
191
|
+
});
|
|
192
|
+
assert.equal(updated.continueHereFired, true, "updated continueHereFired should be true");
|
|
193
|
+
|
|
194
|
+
// Verify persistence
|
|
195
|
+
const readUpdated = readUnitRuntimeRecord(tmpDir, "execute-task", "M007/S02/T02");
|
|
196
|
+
assert.equal(readUpdated!.continueHereFired, true, "persisted continueHereFired should be true");
|
|
197
|
+
|
|
198
|
+
// Clean up
|
|
199
|
+
clearUnitRuntimeRecord(tmpDir, "execute-task", "M007/S02/T02");
|
|
202
200
|
});
|
|
203
201
|
});
|
|
204
202
|
|
|
205
203
|
describe("context-pressure monitor integration", () => {
|
|
206
|
-
it("should fire wrap-up when context >= threshold and mark continueHereFired", async () => {
|
|
204
|
+
it("should fire wrap-up when context >= threshold and mark continueHereFired", async (t) => {
|
|
207
205
|
const { writeUnitRuntimeRecord, readUnitRuntimeRecord, clearUnitRuntimeRecord } = await import("../unit-runtime.js");
|
|
208
206
|
const fs = await import("node:fs");
|
|
209
207
|
const path = await import("node:path");
|
|
210
208
|
const os = await import("node:os");
|
|
211
209
|
|
|
212
210
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "continue-here-monitor-"));
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
254
|
-
}
|
|
211
|
+
t.after(() => fs.rmSync(tmpDir, { recursive: true, force: true }));
|
|
212
|
+
|
|
213
|
+
// Simulate the monitor's one-shot logic:
|
|
214
|
+
// 1. Write initial runtime record (continueHereFired=false)
|
|
215
|
+
const startedAt = Date.now();
|
|
216
|
+
writeUnitRuntimeRecord(tmpDir, "execute-task", "M001/S01/T01", startedAt, {
|
|
217
|
+
phase: "dispatched",
|
|
218
|
+
wrapupWarningSent: false,
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
const budget = computeBudgets(128_000);
|
|
222
|
+
const threshold = budget.continueThresholdPercent;
|
|
223
|
+
|
|
224
|
+
// Simulate the monitor poll: context at 75% (above threshold)
|
|
225
|
+
const contextPercent = 75;
|
|
226
|
+
const runtime = readUnitRuntimeRecord(tmpDir, "execute-task", "M001/S01/T01");
|
|
227
|
+
assert.ok(runtime, "runtime record should exist");
|
|
228
|
+
assert.equal(runtime!.continueHereFired, false, "initially false");
|
|
229
|
+
|
|
230
|
+
// Check: should fire
|
|
231
|
+
const shouldFire = !runtime!.continueHereFired
|
|
232
|
+
&& contextPercent >= threshold;
|
|
233
|
+
assert.ok(shouldFire, "should fire when context >= threshold and not yet fired");
|
|
234
|
+
|
|
235
|
+
// Mark as fired (what the monitor does)
|
|
236
|
+
writeUnitRuntimeRecord(tmpDir, "execute-task", "M001/S01/T01", startedAt, {
|
|
237
|
+
continueHereFired: true,
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
// Verify one-shot: second poll should NOT fire
|
|
241
|
+
const runtime2 = readUnitRuntimeRecord(tmpDir, "execute-task", "M001/S01/T01");
|
|
242
|
+
assert.ok(runtime2, "runtime record should still exist");
|
|
243
|
+
assert.equal(runtime2!.continueHereFired, true, "should be marked as fired");
|
|
244
|
+
|
|
245
|
+
const shouldFireAgain = !runtime2!.continueHereFired
|
|
246
|
+
&& contextPercent >= threshold;
|
|
247
|
+
assert.equal(shouldFireAgain, false, "must not fire again — one-shot guard");
|
|
248
|
+
|
|
249
|
+
// Clean up
|
|
250
|
+
clearUnitRuntimeRecord(tmpDir, "execute-task", "M001/S01/T01");
|
|
255
251
|
});
|
|
256
252
|
|
|
257
253
|
it("should not fire when context is below threshold", () => {
|
|
@@ -7,11 +7,12 @@
|
|
|
7
7
|
* That failure confirms the test runs against real code. (T01 state)
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
+
import { describe, test } from "node:test";
|
|
11
|
+
import assert from "node:assert/strict";
|
|
10
12
|
import {
|
|
11
13
|
type SliceAggregate,
|
|
12
14
|
formatCostProjection,
|
|
13
15
|
} from "../metrics.js";
|
|
14
|
-
import { createTestContext } from './test-helpers.ts';
|
|
15
16
|
|
|
16
17
|
// ─── Test helpers ─────────────────────────────────────────────────────────────
|
|
17
18
|
|
|
@@ -25,110 +26,95 @@ function makeSliceAggregate(sliceId: string, cost: number): SliceAggregate {
|
|
|
25
26
|
};
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
const { assertEq, assertTrue, report } = createTestContext();
|
|
29
29
|
// ─── formatCostProjection ─────────────────────────────────────────────────────
|
|
30
30
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
makeSliceAggregate("M001/S01", 0.10),
|
|
122
|
-
makeSliceAggregate("M001/S02", 0.10),
|
|
123
|
-
];
|
|
124
|
-
const result = formatCostProjection(slices, 3);
|
|
125
|
-
const hasCorrectProjection = result.some(line => line.includes("$0.30"));
|
|
126
|
-
assertTrue(
|
|
127
|
-
hasCorrectProjection,
|
|
128
|
-
"bare milestone entry excluded from avg: projection shows $0.30 (avg $0.10 × 3), not $1.83 (including $5.00 entry)"
|
|
129
|
-
);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
// ─── Summary ──────────────────────────────────────────────────────────────────
|
|
133
|
-
|
|
134
|
-
report();
|
|
31
|
+
describe("formatCostProjection", () => {
|
|
32
|
+
|
|
33
|
+
test("zero completed slices → empty result", () => {
|
|
34
|
+
const result = formatCostProjection([], 3);
|
|
35
|
+
assert.strictEqual(result.length, 0, "zero slices → empty array");
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test("one slice → suppressed (need ≥2 to project reliably)", () => {
|
|
39
|
+
const result = formatCostProjection([makeSliceAggregate("M001/S01", 0.10)], 3);
|
|
40
|
+
assert.strictEqual(result.length, 0, "one slice → suppressed (no projection shown)");
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("two slices → projection shown", () => {
|
|
44
|
+
const slices = [
|
|
45
|
+
makeSliceAggregate("M001/S01", 0.10),
|
|
46
|
+
makeSliceAggregate("M001/S02", 0.10),
|
|
47
|
+
];
|
|
48
|
+
const result = formatCostProjection(slices, 5);
|
|
49
|
+
assert.ok(result.length > 0, "two slices → projection shown");
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test("two-slice result contains $ (cost is formatted)", () => {
|
|
53
|
+
const slices = [
|
|
54
|
+
makeSliceAggregate("M001/S01", 0.10),
|
|
55
|
+
makeSliceAggregate("M001/S02", 0.10),
|
|
56
|
+
];
|
|
57
|
+
const result = formatCostProjection(slices, 5);
|
|
58
|
+
assert.ok(result.length > 0 && result[0].includes("$"), "projection line contains \"$\"");
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test("budget ceiling hit: total >= ceiling → line contains ceiling", () => {
|
|
62
|
+
const slices = [
|
|
63
|
+
makeSliceAggregate("M001/S01", 0.10),
|
|
64
|
+
makeSliceAggregate("M001/S02", 0.10),
|
|
65
|
+
];
|
|
66
|
+
const result = formatCostProjection(slices, 5, 0.05);
|
|
67
|
+
const hasCeilingLine = result.some(
|
|
68
|
+
line => line.toLowerCase().includes("ceiling")
|
|
69
|
+
);
|
|
70
|
+
assert.ok(hasCeilingLine, "ceiling warning appears when total ($0.20) >= ceiling ($0.05)");
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test("budget ceiling not hit: total < ceiling → no ceiling line", () => {
|
|
74
|
+
const slices = [
|
|
75
|
+
makeSliceAggregate("M001/S01", 0.10),
|
|
76
|
+
makeSliceAggregate("M001/S02", 0.10),
|
|
77
|
+
];
|
|
78
|
+
const result = formatCostProjection(slices, 5, 100.00);
|
|
79
|
+
const hasCeilingLine = result.some(
|
|
80
|
+
line => line.toLowerCase().includes("ceiling")
|
|
81
|
+
);
|
|
82
|
+
assert.ok(!hasCeilingLine, "no ceiling warning when total ($0.20) < ceiling ($100.00)");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("no ceiling arg → no ceiling line", () => {
|
|
86
|
+
const slices = [
|
|
87
|
+
makeSliceAggregate("M001/S01", 0.10),
|
|
88
|
+
makeSliceAggregate("M001/S02", 0.10),
|
|
89
|
+
];
|
|
90
|
+
const result = formatCostProjection(slices, 5);
|
|
91
|
+
const hasCeilingLine = result.some(
|
|
92
|
+
line => line.toLowerCase().includes("ceiling")
|
|
93
|
+
);
|
|
94
|
+
assert.ok(!hasCeilingLine, "no ceiling warning when no ceiling is set");
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("rounding: avg $0.10 × 5 remaining = $0.50", () => {
|
|
98
|
+
const slices = [
|
|
99
|
+
makeSliceAggregate("M001/S01", 0.10),
|
|
100
|
+
makeSliceAggregate("M001/S02", 0.10),
|
|
101
|
+
];
|
|
102
|
+
const result = formatCostProjection(slices, 5);
|
|
103
|
+
const hasRoundedCost = result.some(line => line.includes("$0.50"));
|
|
104
|
+
assert.ok(hasRoundedCost, "projected cost $0.50 (avg $0.10 × 5 remaining) appears in output");
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("bare milestone entries excluded from average", () => {
|
|
108
|
+
const slices = [
|
|
109
|
+
makeSliceAggregate("M001", 5.00), // bare milestone — must be excluded
|
|
110
|
+
makeSliceAggregate("M001/S01", 0.10),
|
|
111
|
+
makeSliceAggregate("M001/S02", 0.10),
|
|
112
|
+
];
|
|
113
|
+
const result = formatCostProjection(slices, 3);
|
|
114
|
+
const hasCorrectProjection = result.some(line => line.includes("$0.30"));
|
|
115
|
+
assert.ok(
|
|
116
|
+
hasCorrectProjection,
|
|
117
|
+
"bare milestone entry excluded from avg: projection shows $0.30 (avg $0.10 × 3), not $1.83 (including $5.00 entry)"
|
|
118
|
+
);
|
|
119
|
+
});
|
|
120
|
+
});
|
|
@@ -26,53 +26,45 @@ function cleanup(base: string): void {
|
|
|
26
26
|
|
|
27
27
|
// ─── writeLock / readCrashLock ────────────────────────────────────────────
|
|
28
28
|
|
|
29
|
-
test("writeLock creates lock file and readCrashLock reads it", () => {
|
|
29
|
+
test("writeLock creates lock file and readCrashLock reads it", (t) => {
|
|
30
30
|
const base = makeTmpBase();
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
cleanup(base);
|
|
42
|
-
}
|
|
31
|
+
t.after(() => cleanup(base));
|
|
32
|
+
|
|
33
|
+
writeLock(base, "execute-task", "M001/S01/T01", 3, "/tmp/session.jsonl");
|
|
34
|
+
const lock = readCrashLock(base);
|
|
35
|
+
assert.ok(lock, "lock should exist");
|
|
36
|
+
assert.equal(lock!.unitType, "execute-task");
|
|
37
|
+
assert.equal(lock!.unitId, "M001/S01/T01");
|
|
38
|
+
assert.equal(lock!.completedUnits, 3);
|
|
39
|
+
assert.equal(lock!.sessionFile, "/tmp/session.jsonl");
|
|
40
|
+
assert.equal(lock!.pid, process.pid);
|
|
43
41
|
});
|
|
44
42
|
|
|
45
|
-
test("readCrashLock returns null when no lock exists", () => {
|
|
43
|
+
test("readCrashLock returns null when no lock exists", (t) => {
|
|
46
44
|
const base = makeTmpBase();
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
cleanup(base);
|
|
52
|
-
}
|
|
45
|
+
t.after(() => cleanup(base));
|
|
46
|
+
|
|
47
|
+
const lock = readCrashLock(base);
|
|
48
|
+
assert.equal(lock, null);
|
|
53
49
|
});
|
|
54
50
|
|
|
55
51
|
// ─── clearLock ────────────────────────────────────────────────────────────
|
|
56
52
|
|
|
57
|
-
test("clearLock removes existing lock file", () => {
|
|
53
|
+
test("clearLock removes existing lock file", (t) => {
|
|
58
54
|
const base = makeTmpBase();
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
cleanup(base);
|
|
66
|
-
}
|
|
55
|
+
t.after(() => cleanup(base));
|
|
56
|
+
|
|
57
|
+
writeLock(base, "plan-slice", "M001/S01", 0);
|
|
58
|
+
assert.ok(readCrashLock(base), "lock should exist before clear");
|
|
59
|
+
clearLock(base);
|
|
60
|
+
assert.equal(readCrashLock(base), null, "lock should be gone after clear");
|
|
67
61
|
});
|
|
68
62
|
|
|
69
|
-
test("clearLock is safe when no lock exists", () => {
|
|
63
|
+
test("clearLock is safe when no lock exists", (t) => {
|
|
70
64
|
const base = makeTmpBase();
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
cleanup(base);
|
|
75
|
-
}
|
|
65
|
+
t.after(() => cleanup(base));
|
|
66
|
+
|
|
67
|
+
assert.doesNotThrow(() => clearLock(base));
|
|
76
68
|
});
|
|
77
69
|
|
|
78
70
|
// ─── isLockProcessAlive ──────────────────────────────────────────────────
|