gsd-pi 2.44.0-dev.62b5d6c → 2.44.0-dev.a5271fc
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/bootstrap/system-context.js +46 -12
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +5 -0
- package/dist/resources/extensions/gsd/preferences.js +9 -1
- package/dist/resources/extensions/gsd/state.js +19 -2
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +19 -19
- 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 +19 -19
- 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/bootstrap/system-context.ts +48 -11
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +8 -0
- package/src/resources/extensions/gsd/preferences.ts +11 -1
- package/src/resources/extensions/gsd/state.ts +19 -1
- 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 +183 -181
- 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/knowledge.test.ts +89 -0
- 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/preferences.test.ts +27 -0
- 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 → JyimLR2pZuvKEzv26gI3w}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{fOnWQBjWXMKUs4bqTg530 → JyimLR2pZuvKEzv26gI3w}/_ssgManifest.js +0 -0
|
@@ -19,9 +19,9 @@ import type {
|
|
|
19
19
|
GSDSlice,
|
|
20
20
|
GSDTask,
|
|
21
21
|
} from '../migrate/types.ts';
|
|
22
|
-
import {
|
|
22
|
+
import { describe, test, beforeEach, afterEach } from 'node:test';
|
|
23
|
+
import assert from 'node:assert/strict';
|
|
23
24
|
|
|
24
|
-
const { assertEq, assertTrue, report } = createTestContext();
|
|
25
25
|
// ─── Fixture Helpers ───────────────────────────────────────────────────────
|
|
26
26
|
|
|
27
27
|
function emptyProject(overrides: Partial<PlanningProject> = {}): PlanningProject {
|
|
@@ -134,8 +134,7 @@ function makeResearch(fileName: string, content: string): PlanningResearch {
|
|
|
134
134
|
|
|
135
135
|
// ─── Scenario 1: Flat Single-Milestone (3 phases → M001 with S01/S02/S03) ──
|
|
136
136
|
|
|
137
|
-
{
|
|
138
|
-
console.log('Scenario 1: Flat single-milestone');
|
|
137
|
+
test('Scenario 1: Flat single-milestone', () => {
|
|
139
138
|
|
|
140
139
|
const project = emptyProject({
|
|
141
140
|
project: '# My Project\nA cool project.',
|
|
@@ -159,26 +158,25 @@ function makeResearch(fileName: string, content: string): PlanningResearch {
|
|
|
159
158
|
|
|
160
159
|
const result = transformToGSD(project);
|
|
161
160
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
}
|
|
161
|
+
assert.deepStrictEqual(result.milestones.length, 1, 'flat: produces 1 milestone');
|
|
162
|
+
assert.ok(result.milestones[0]?.id === 'M001', 'flat: milestone ID is M001');
|
|
163
|
+
assert.deepStrictEqual(result.milestones[0]?.slices.length, 3, 'flat: 3 slices');
|
|
164
|
+
assert.deepStrictEqual(result.milestones[0]?.slices[0]?.id, 'S01', 'flat: first slice is S01');
|
|
165
|
+
assert.deepStrictEqual(result.milestones[0]?.slices[1]?.id, 'S02', 'flat: second slice is S02');
|
|
166
|
+
assert.deepStrictEqual(result.milestones[0]?.slices[2]?.id, 'S03', 'flat: third slice is S03');
|
|
167
|
+
assert.ok(result.milestones[0]?.slices[0]?.title.length > 0, 'flat: slice title not empty');
|
|
168
|
+
assert.deepStrictEqual(result.milestones[0]?.slices[0]?.tasks.length, 1, 'flat: S01 has 1 task');
|
|
169
|
+
assert.deepStrictEqual(result.milestones[0]?.slices[1]?.tasks.length, 2, 'flat: S02 has 2 tasks');
|
|
170
|
+
assert.deepStrictEqual(result.milestones[0]?.slices[2]?.tasks.length, 1, 'flat: S03 has 1 task');
|
|
171
|
+
assert.deepStrictEqual(result.milestones[0]?.slices[0]?.tasks[0]?.id, 'T01', 'flat: first task is T01');
|
|
172
|
+
assert.deepStrictEqual(result.milestones[0]?.slices[1]?.tasks[1]?.id, 'T02', 'flat: second task in S02 is T02');
|
|
173
|
+
assert.ok(result.projectContent.includes('My Project'), 'flat: projectContent preserved');
|
|
174
|
+
assert.deepStrictEqual(result.milestones[0]?.boundaryMap, [], 'flat: boundaryMap defaults to empty');
|
|
175
|
+
});
|
|
177
176
|
|
|
178
177
|
// ─── Scenario 2: Multi-Milestone (2 milestones with independent numbering) ──
|
|
179
178
|
|
|
180
|
-
{
|
|
181
|
-
console.log('Scenario 2: Multi-milestone');
|
|
179
|
+
test('Scenario 2: Multi-milestone', () => {
|
|
182
180
|
|
|
183
181
|
const project = emptyProject({
|
|
184
182
|
roadmap: milestoneRoadmap([
|
|
@@ -206,23 +204,22 @@ function makeResearch(fileName: string, content: string): PlanningResearch {
|
|
|
206
204
|
|
|
207
205
|
const result = transformToGSD(project);
|
|
208
206
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
207
|
+
assert.deepStrictEqual(result.milestones.length, 2, 'multi: 2 milestones');
|
|
208
|
+
assert.deepStrictEqual(result.milestones[0]?.id, 'M001', 'multi: first milestone M001');
|
|
209
|
+
assert.deepStrictEqual(result.milestones[1]?.id, 'M002', 'multi: second milestone M002');
|
|
210
|
+
assert.deepStrictEqual(result.milestones[0]?.slices.length, 2, 'multi: M001 has 2 slices');
|
|
211
|
+
assert.deepStrictEqual(result.milestones[1]?.slices.length, 3, 'multi: M002 has 3 slices');
|
|
214
212
|
// Independent numbering: both start at S01
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
}
|
|
213
|
+
assert.deepStrictEqual(result.milestones[0]?.slices[0]?.id, 'S01', 'multi: M001 starts at S01');
|
|
214
|
+
assert.deepStrictEqual(result.milestones[1]?.slices[0]?.id, 'S01', 'multi: M002 starts at S01');
|
|
215
|
+
assert.deepStrictEqual(result.milestones[1]?.slices[2]?.id, 'S03', 'multi: M002 third slice is S03');
|
|
216
|
+
assert.ok(result.milestones[0]?.title.length > 0, 'multi: M001 has title');
|
|
217
|
+
assert.ok(result.milestones[1]?.title.length > 0, 'multi: M002 has title');
|
|
218
|
+
});
|
|
221
219
|
|
|
222
220
|
// ─── Scenario 3: Decimal Phase Ordering (1, 2, 2.1, 2.2, 3 → S01–S05) ──
|
|
223
221
|
|
|
224
|
-
{
|
|
225
|
-
console.log('Scenario 3: Decimal phase ordering');
|
|
222
|
+
test('Scenario 3: Decimal phase ordering', () => {
|
|
226
223
|
|
|
227
224
|
const project = emptyProject({
|
|
228
225
|
roadmap: flatRoadmap([
|
|
@@ -243,27 +240,26 @@ function makeResearch(fileName: string, content: string): PlanningResearch {
|
|
|
243
240
|
|
|
244
241
|
const result = transformToGSD(project);
|
|
245
242
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
243
|
+
assert.deepStrictEqual(result.milestones[0]?.slices.length, 5, 'decimal: 5 slices total');
|
|
244
|
+
assert.deepStrictEqual(result.milestones[0]?.slices[0]?.id, 'S01', 'decimal: first is S01');
|
|
245
|
+
assert.deepStrictEqual(result.milestones[0]?.slices[1]?.id, 'S02', 'decimal: second is S02');
|
|
246
|
+
assert.deepStrictEqual(result.milestones[0]?.slices[2]?.id, 'S03', 'decimal: third is S03');
|
|
247
|
+
assert.deepStrictEqual(result.milestones[0]?.slices[3]?.id, 'S04', 'decimal: fourth is S04');
|
|
248
|
+
assert.deepStrictEqual(result.milestones[0]?.slices[4]?.id, 'S05', 'decimal: fifth is S05');
|
|
252
249
|
// Order must be by float value: 1, 2, 2.1, 2.2, 3
|
|
253
|
-
|
|
250
|
+
assert.ok(
|
|
254
251
|
result.milestones[0]?.slices[0]?.title.toLowerCase().includes('foundation'),
|
|
255
252
|
'decimal: S01 is foundation (phase 1)',
|
|
256
253
|
);
|
|
257
|
-
|
|
254
|
+
assert.ok(
|
|
258
255
|
result.milestones[0]?.slices[4]?.title.toLowerCase().includes('finalize'),
|
|
259
256
|
'decimal: S05 is finalize (phase 3)',
|
|
260
257
|
);
|
|
261
|
-
}
|
|
258
|
+
});
|
|
262
259
|
|
|
263
260
|
// ─── Scenario 4: Completion State ──────────────────────────────────────────
|
|
264
261
|
|
|
265
|
-
{
|
|
266
|
-
console.log('Scenario 4: Completion state mapping');
|
|
262
|
+
test('Scenario 4: Completion state mapping', () => {
|
|
267
263
|
|
|
268
264
|
const project = emptyProject({
|
|
269
265
|
roadmap: flatRoadmap([
|
|
@@ -288,26 +284,25 @@ function makeResearch(fileName: string, content: string): PlanningResearch {
|
|
|
288
284
|
const doneSlice = result.milestones[0]?.slices[0];
|
|
289
285
|
const activeSlice = result.milestones[0]?.slices[1];
|
|
290
286
|
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
}
|
|
287
|
+
assert.ok(doneSlice?.done === true, 'completion: done phase → done slice');
|
|
288
|
+
assert.ok(activeSlice?.done === false, 'completion: active phase → not-done slice');
|
|
289
|
+
assert.ok(doneSlice?.tasks[0]?.done === true, 'completion: plan with summary → done task');
|
|
290
|
+
assert.ok(doneSlice?.tasks[1]?.done === false, 'completion: plan without summary → not-done task');
|
|
291
|
+
assert.ok(doneSlice?.tasks[0]?.summary !== null, 'completion: done task has summary data');
|
|
292
|
+
assert.ok(doneSlice?.tasks[1]?.summary === null, 'completion: not-done task has null summary');
|
|
293
|
+
assert.deepStrictEqual(doneSlice?.tasks[0]?.summary?.completedAt, '2026-01-15', 'completion: summary completedAt from frontmatter');
|
|
294
|
+
assert.deepStrictEqual(doneSlice?.tasks[0]?.summary?.duration, '2h', 'completion: summary duration from frontmatter');
|
|
295
|
+
assert.deepStrictEqual(doneSlice?.tasks[0]?.summary?.provides, ['feature-01'], 'completion: summary provides from frontmatter');
|
|
296
|
+
assert.deepStrictEqual(doneSlice?.tasks[0]?.summary?.keyFiles, ['file-01.ts'], 'completion: summary keyFiles from frontmatter');
|
|
297
|
+
assert.ok(doneSlice?.tasks[0]?.summary?.whatHappened?.includes('Summary body') ?? false, 'completion: summary whatHappened from body');
|
|
298
|
+
assert.ok(doneSlice?.summary !== null, 'completion: done slice has slice summary');
|
|
299
|
+
assert.ok(activeSlice?.summary === null, 'completion: active slice has null summary');
|
|
300
|
+
assert.deepStrictEqual(doneSlice?.tasks[0]?.estimate, '2h', 'completion: task estimate from summary duration');
|
|
301
|
+
});
|
|
306
302
|
|
|
307
303
|
// ─── Scenario 5: Research Consolidation ────────────────────────────────────
|
|
308
304
|
|
|
309
|
-
{
|
|
310
|
-
console.log('Scenario 5: Research consolidation');
|
|
305
|
+
test('Scenario 5: Research consolidation', () => {
|
|
311
306
|
|
|
312
307
|
const project = emptyProject({
|
|
313
308
|
roadmap: flatRoadmap([roadmapEntry(1, 'researched-phase')]),
|
|
@@ -328,28 +323,27 @@ function makeResearch(fileName: string, content: string): PlanningResearch {
|
|
|
328
323
|
const result = transformToGSD(project);
|
|
329
324
|
|
|
330
325
|
// Project-level research → milestone research
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
326
|
+
assert.ok(result.milestones[0]?.research !== null, 'research: milestone has consolidated research');
|
|
327
|
+
assert.ok(result.milestones[0]?.research!.includes('Project Summary'), 'research: includes SUMMARY content');
|
|
328
|
+
assert.ok(result.milestones[0]?.research!.includes('Architecture'), 'research: includes ARCHITECTURE content');
|
|
329
|
+
assert.ok(result.milestones[0]?.research!.includes('Pitfalls'), 'research: includes PITFALLS content');
|
|
335
330
|
|
|
336
331
|
// Fixed ordering: SUMMARY before ARCHITECTURE before PITFALLS
|
|
337
332
|
const summaryIdx = result.milestones[0]?.research!.indexOf('Project Summary') ?? -1;
|
|
338
333
|
const archIdx = result.milestones[0]?.research!.indexOf('Architecture') ?? -1;
|
|
339
334
|
const pitfallIdx = result.milestones[0]?.research!.indexOf('Pitfalls') ?? -1;
|
|
340
|
-
|
|
341
|
-
|
|
335
|
+
assert.ok(summaryIdx < archIdx, 'research: SUMMARY before ARCHITECTURE in consolidated');
|
|
336
|
+
assert.ok(archIdx < pitfallIdx, 'research: ARCHITECTURE before PITFALLS in consolidated');
|
|
342
337
|
|
|
343
338
|
// Phase-level research → slice research
|
|
344
339
|
const slice = result.milestones[0]?.slices[0];
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
}
|
|
340
|
+
assert.ok(slice?.research !== null, 'research: slice has phase research');
|
|
341
|
+
assert.ok(slice?.research!.includes('Phase Features'), 'research: slice research includes phase content');
|
|
342
|
+
});
|
|
348
343
|
|
|
349
344
|
// ─── Scenario 6: Requirements Classification ──────────────────────────────
|
|
350
345
|
|
|
351
|
-
{
|
|
352
|
-
console.log('Scenario 6: Requirements classification');
|
|
346
|
+
test('Scenario 6: Requirements classification', () => {
|
|
353
347
|
|
|
354
348
|
const project = emptyProject({
|
|
355
349
|
roadmap: flatRoadmap([roadmapEntry(1, 'req-phase')]),
|
|
@@ -365,22 +359,21 @@ function makeResearch(fileName: string, content: string): PlanningResearch {
|
|
|
365
359
|
|
|
366
360
|
const result = transformToGSD(project);
|
|
367
361
|
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
}
|
|
362
|
+
assert.deepStrictEqual(result.requirements.length, 3, 'requirements: 3 requirements');
|
|
363
|
+
assert.deepStrictEqual(result.requirements[0]?.id, 'R001', 'requirements: first is R001');
|
|
364
|
+
assert.deepStrictEqual(result.requirements[0]?.status, 'active', 'requirements: R001 status active');
|
|
365
|
+
assert.deepStrictEqual(result.requirements[1]?.status, 'validated', 'requirements: R002 status validated');
|
|
366
|
+
assert.deepStrictEqual(result.requirements[2]?.status, 'deferred', 'requirements: R003 status deferred');
|
|
367
|
+
assert.ok(result.requirements[0]?.title === 'Core Feature', 'requirements: R001 title preserved');
|
|
368
|
+
assert.ok(result.requirements[0]?.description.includes('Description for R001'), 'requirements: R001 description preserved');
|
|
369
|
+
assert.deepStrictEqual(result.requirements[0]?.class, 'core-capability', 'requirements: default class');
|
|
370
|
+
assert.deepStrictEqual(result.requirements[0]?.source, 'inferred', 'requirements: default source');
|
|
371
|
+
assert.deepStrictEqual(result.requirements[0]?.primarySlice, 'none yet', 'requirements: default primarySlice');
|
|
372
|
+
});
|
|
379
373
|
|
|
380
374
|
// ─── Scenario 7: Empty Phase (no plans → slice with 0 tasks) ───────────────
|
|
381
375
|
|
|
382
|
-
{
|
|
383
|
-
console.log('Scenario 7: Empty phase');
|
|
376
|
+
test('Scenario 7: Empty phase', () => {
|
|
384
377
|
|
|
385
378
|
const project = emptyProject({
|
|
386
379
|
roadmap: flatRoadmap([
|
|
@@ -397,15 +390,14 @@ function makeResearch(fileName: string, content: string): PlanningResearch {
|
|
|
397
390
|
|
|
398
391
|
const result = transformToGSD(project);
|
|
399
392
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
}
|
|
393
|
+
assert.deepStrictEqual(result.milestones[0]?.slices[0]?.tasks.length, 0, 'empty: empty phase → 0 tasks');
|
|
394
|
+
assert.deepStrictEqual(result.milestones[0]?.slices[1]?.tasks.length, 1, 'empty: non-empty phase → 1 task');
|
|
395
|
+
assert.ok(result.milestones[0]?.slices[0]?.id === 'S01', 'empty: empty slice still gets ID');
|
|
396
|
+
});
|
|
404
397
|
|
|
405
398
|
// ─── Scenario 8: Demo Derivation from Plan Objective ───────────────────────
|
|
406
399
|
|
|
407
|
-
{
|
|
408
|
-
console.log('Scenario 8: Demo derivation');
|
|
400
|
+
test('Scenario 8: Demo derivation', () => {
|
|
409
401
|
|
|
410
402
|
const project = emptyProject({
|
|
411
403
|
roadmap: flatRoadmap([roadmapEntry(1, 'demo-phase')]),
|
|
@@ -420,19 +412,18 @@ function makeResearch(fileName: string, content: string): PlanningResearch {
|
|
|
420
412
|
|
|
421
413
|
const result = transformToGSD(project);
|
|
422
414
|
|
|
423
|
-
|
|
424
|
-
|
|
415
|
+
assert.ok(result.milestones[0]?.slices[0]?.demo.length > 0, 'demo: slice demo is not empty');
|
|
416
|
+
assert.ok(
|
|
425
417
|
result.milestones[0]?.slices[0]?.demo.includes('authentication') ||
|
|
426
418
|
result.milestones[0]?.slices[0]?.demo.includes('Build'),
|
|
427
419
|
'demo: slice demo derived from first plan objective',
|
|
428
420
|
);
|
|
429
|
-
|
|
430
|
-
}
|
|
421
|
+
assert.ok(result.milestones[0]?.slices[0]?.goal.length > 0, 'demo: slice goal is not empty');
|
|
422
|
+
});
|
|
431
423
|
|
|
432
424
|
// ─── Scenario 9: Field Defaults and Type Safety ────────────────────────────
|
|
433
425
|
|
|
434
|
-
{
|
|
435
|
-
console.log('Scenario 9: Field defaults');
|
|
426
|
+
test('Scenario 9: Field defaults', () => {
|
|
436
427
|
|
|
437
428
|
const project = emptyProject({
|
|
438
429
|
roadmap: flatRoadmap([roadmapEntry(1, 'defaults-phase')]),
|
|
@@ -460,20 +451,19 @@ function makeResearch(fileName: string, content: string): PlanningResearch {
|
|
|
460
451
|
const slice = result.milestones[0]?.slices[0];
|
|
461
452
|
const task = slice?.tasks[0];
|
|
462
453
|
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
}
|
|
454
|
+
assert.deepStrictEqual(slice?.risk, 'medium', 'defaults: slice risk defaults to medium');
|
|
455
|
+
assert.deepStrictEqual(slice?.depends, [], 'defaults: S01 has no depends');
|
|
456
|
+
assert.ok(task?.description.length > 0, 'defaults: task description not empty');
|
|
457
|
+
assert.deepStrictEqual(task?.files, ['src/auth.ts', 'src/db.ts'], 'defaults: task files from frontmatter');
|
|
458
|
+
assert.deepStrictEqual(task?.mustHaves, ['Auth works', 'DB connected'], 'defaults: task mustHaves from frontmatter');
|
|
459
|
+
assert.deepStrictEqual(task?.done, false, 'defaults: task without summary is not done');
|
|
460
|
+
assert.deepStrictEqual(task?.estimate, '', 'defaults: task without summary has empty estimate');
|
|
461
|
+
assert.ok(task?.summary === null, 'defaults: task without summary has null summary');
|
|
462
|
+
});
|
|
472
463
|
|
|
473
464
|
// ─── Scenario 10: Sequential Depends ──────────────────────────────────────
|
|
474
465
|
|
|
475
|
-
{
|
|
476
|
-
console.log('Scenario 10: Sequential depends');
|
|
466
|
+
test('Scenario 10: Sequential depends', () => {
|
|
477
467
|
|
|
478
468
|
const project = emptyProject({
|
|
479
469
|
roadmap: flatRoadmap([
|
|
@@ -491,15 +481,14 @@ function makeResearch(fileName: string, content: string): PlanningResearch {
|
|
|
491
481
|
const result = transformToGSD(project);
|
|
492
482
|
const slices = result.milestones[0]?.slices;
|
|
493
483
|
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
}
|
|
484
|
+
assert.deepStrictEqual(slices?.[0]?.depends, [], 'depends: S01 has empty depends');
|
|
485
|
+
assert.deepStrictEqual(slices?.[1]?.depends, ['S01'], 'depends: S02 depends on S01');
|
|
486
|
+
assert.deepStrictEqual(slices?.[2]?.depends, ['S02'], 'depends: S03 depends on S02');
|
|
487
|
+
});
|
|
498
488
|
|
|
499
489
|
// ─── Scenario 11: Requirements with unknown status and missing IDs ─────────
|
|
500
490
|
|
|
501
|
-
{
|
|
502
|
-
console.log('Scenario 11: Requirements edge cases');
|
|
491
|
+
test('Scenario 11: Requirements edge cases', () => {
|
|
503
492
|
|
|
504
493
|
const project = emptyProject({
|
|
505
494
|
roadmap: flatRoadmap([roadmapEntry(1, 'req-edge')]),
|
|
@@ -516,17 +505,16 @@ function makeResearch(fileName: string, content: string): PlanningResearch {
|
|
|
516
505
|
|
|
517
506
|
const result = transformToGSD(project);
|
|
518
507
|
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
}
|
|
508
|
+
assert.deepStrictEqual(result.requirements[0]?.id, 'R001', 'req-edge: empty id gets R001');
|
|
509
|
+
assert.deepStrictEqual(result.requirements[1]?.id, 'R002', 'req-edge: second empty id gets R002');
|
|
510
|
+
assert.deepStrictEqual(result.requirements[2]?.id, 'R005', 'req-edge: existing id preserved');
|
|
511
|
+
assert.deepStrictEqual(result.requirements[2]?.status, 'active', 'req-edge: unknown status normalized to active');
|
|
512
|
+
assert.deepStrictEqual(result.requirements[3]?.status, 'deferred', 'req-edge: uppercase DEFERRED normalized');
|
|
513
|
+
});
|
|
525
514
|
|
|
526
515
|
// ─── Scenario 12: Vision derivation ────────────────────────────────────────
|
|
527
516
|
|
|
528
|
-
{
|
|
529
|
-
console.log('Scenario 12: Vision derivation');
|
|
517
|
+
test('Scenario 12: Vision derivation', () => {
|
|
530
518
|
|
|
531
519
|
// Vision from project description
|
|
532
520
|
const project1 = emptyProject({
|
|
@@ -536,7 +524,7 @@ function makeResearch(fileName: string, content: string): PlanningResearch {
|
|
|
536
524
|
});
|
|
537
525
|
|
|
538
526
|
const result1 = transformToGSD(project1);
|
|
539
|
-
|
|
527
|
+
assert.ok(result1.milestones[0]?.vision.includes('revolutionary'), 'vision: derived from project first line');
|
|
540
528
|
|
|
541
529
|
// Vision fallback when no project
|
|
542
530
|
const project2 = emptyProject({
|
|
@@ -545,13 +533,12 @@ function makeResearch(fileName: string, content: string): PlanningResearch {
|
|
|
545
533
|
});
|
|
546
534
|
|
|
547
535
|
const result2 = transformToGSD(project2);
|
|
548
|
-
|
|
549
|
-
}
|
|
536
|
+
assert.ok(result2.milestones[0]?.vision.length > 0, 'vision: fallback is non-empty');
|
|
537
|
+
});
|
|
550
538
|
|
|
551
539
|
// ─── Scenario 13: Decisions content from summaries ─────────────────────────
|
|
552
540
|
|
|
553
|
-
{
|
|
554
|
-
console.log('Scenario 13: Decisions content');
|
|
541
|
+
test('Scenario 13: Decisions content', () => {
|
|
555
542
|
|
|
556
543
|
const project = emptyProject({
|
|
557
544
|
roadmap: flatRoadmap([roadmapEntry(1, 'decision-phase', true)]),
|
|
@@ -565,13 +552,12 @@ function makeResearch(fileName: string, content: string): PlanningResearch {
|
|
|
565
552
|
|
|
566
553
|
const result = transformToGSD(project);
|
|
567
554
|
|
|
568
|
-
|
|
569
|
-
}
|
|
555
|
+
assert.ok(result.decisionsContent.includes('decision-01'), 'decisions: extracts key-decisions from summaries');
|
|
556
|
+
});
|
|
570
557
|
|
|
571
558
|
// ─── Scenario 14: No undefined values in output ───────────────────────────
|
|
572
559
|
|
|
573
|
-
{
|
|
574
|
-
console.log('Scenario 14: No undefined values');
|
|
560
|
+
test('Scenario 14: No undefined values', () => {
|
|
575
561
|
|
|
576
562
|
const project = emptyProject({
|
|
577
563
|
project: '# Test\nDescription.',
|
|
@@ -596,7 +582,7 @@ function makeResearch(fileName: string, content: string): PlanningResearch {
|
|
|
596
582
|
// Deep check for undefined values
|
|
597
583
|
function checkNoUndefined(obj: unknown, path: string): void {
|
|
598
584
|
if (obj === undefined) {
|
|
599
|
-
|
|
585
|
+
assert.ok(false, `no-undefined: ${path} is undefined`);
|
|
600
586
|
return;
|
|
601
587
|
}
|
|
602
588
|
if (obj === null) return; // null is allowed (e.g. research, summary)
|
|
@@ -612,13 +598,12 @@ function makeResearch(fileName: string, content: string): PlanningResearch {
|
|
|
612
598
|
}
|
|
613
599
|
|
|
614
600
|
checkNoUndefined(result, 'result');
|
|
615
|
-
|
|
616
|
-
}
|
|
601
|
+
assert.ok(true, 'no-undefined: deep check completed without finding undefined values');
|
|
602
|
+
});
|
|
617
603
|
|
|
618
604
|
// ─── Scenario 15: Research with no files ───────────────────────────────────
|
|
619
605
|
|
|
620
|
-
{
|
|
621
|
-
console.log('Scenario 15: Empty research');
|
|
606
|
+
test('Scenario 15: Empty research', () => {
|
|
622
607
|
|
|
623
608
|
const project = emptyProject({
|
|
624
609
|
roadmap: flatRoadmap([roadmapEntry(1, 'no-research')]),
|
|
@@ -626,10 +611,9 @@ function makeResearch(fileName: string, content: string): PlanningResearch {
|
|
|
626
611
|
});
|
|
627
612
|
|
|
628
613
|
const result = transformToGSD(project);
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
}
|
|
614
|
+
assert.ok(result.milestones[0]?.research === null, 'empty-research: milestone research is null');
|
|
615
|
+
assert.ok(result.milestones[0]?.slices[0]?.research === null, 'empty-research: slice research is null');
|
|
616
|
+
});
|
|
632
617
|
|
|
633
618
|
// ─── Results ───────────────────────────────────────────────────────────────
|
|
634
619
|
|
|
635
|
-
report();
|