gsd-pi 2.44.0-dev.848dd4c → 2.44.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/README.md +12 -30
- package/dist/resources/extensions/gsd/auto-start.js +0 -10
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +0 -5
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +14 -14
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/required-server-files.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- 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/page.js +2 -2
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +3 -3
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +3 -3
- 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 +3 -3
- 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/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +5 -5
- package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -4
- 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 +3 -3
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +14 -14
- package/dist/web/standalone/.next/server/chunks/229.js +1 -1
- package/dist/web/standalone/.next/server/chunks/471.js +3 -3
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware.js +2 -2
- package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
- package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
- 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/dist/web/standalone/.next/static/chunks/app/_not-found/{page-f2a7482d42a5614b.js → page-2f24283c162b6ab3.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/{layout-a16c7a7ecdf0c2cf.js → layout-9ecfd95f343793f0.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-7e9530a7122506c5.js +1 -0
- package/dist/web/standalone/.next/static/chunks/main-app-d3d4c336195465f9.js +1 -0
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ab5a8926e07ec673.js +1 -0
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_except.target.mk +14 -14
- package/dist/web/standalone/node_modules/node-pty/node-addon-api/node_addon_api_maybe.target.mk +14 -14
- package/dist/web/standalone/server.js +1 -1
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/auth-storage.test.js +8 -6
- 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 +26 -24
- 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 +48 -29
- 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 +44 -34
- 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 +34 -30
- 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 +12 -10
- 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 +47 -43
- 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 +43 -31
- package/packages/pi-coding-agent/src/core/resolve-config-value.test.ts +45 -40
- 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 +0 -14
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +0 -8
- 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 +16 -14
- package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +57 -43
- package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +13 -11
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +523 -465
- package/src/resources/extensions/gsd/tests/auto-secrets-gate.test.ts +75 -73
- package/src/resources/extensions/gsd/tests/auto-start-needs-discussion.test.ts +56 -34
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +656 -533
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +143 -165
- package/src/resources/extensions/gsd/tests/cache-staleness-regression.test.ts +52 -29
- package/src/resources/extensions/gsd/tests/captures.test.ts +176 -148
- package/src/resources/extensions/gsd/tests/claude-import-tui.test.ts +33 -32
- package/src/resources/extensions/gsd/tests/collect-from-manifest.test.ts +143 -141
- 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 +59 -38
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +263 -228
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +302 -250
- package/src/resources/extensions/gsd/tests/context-store.test.ts +367 -354
- package/src/resources/extensions/gsd/tests/continue-here.test.ts +72 -68
- package/src/resources/extensions/gsd/tests/cost-projection.test.ts +106 -92
- package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +35 -27
- package/src/resources/extensions/gsd/tests/dashboard-budget.test.ts +237 -220
- package/src/resources/extensions/gsd/tests/db-writer.test.ts +420 -390
- package/src/resources/extensions/gsd/tests/definition-loader.test.ts +92 -76
- package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +83 -68
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +183 -152
- package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +101 -78
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +227 -192
- package/src/resources/extensions/gsd/tests/detection.test.ts +278 -232
- package/src/resources/extensions/gsd/tests/dev-engine-wrapper.test.ts +34 -30
- package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +180 -164
- package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +49 -43
- package/src/resources/extensions/gsd/tests/dispatch-uat-last-completed.test.ts +32 -28
- package/src/resources/extensions/gsd/tests/doctor-completion-deferral.test.ts +29 -27
- package/src/resources/extensions/gsd/tests/doctor-delimiter-fix.test.ts +38 -34
- package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +75 -54
- package/src/resources/extensions/gsd/tests/doctor-environment-worktree.test.ts +32 -21
- package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +97 -72
- package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +44 -38
- package/src/resources/extensions/gsd/tests/doctor-git.test.ts +145 -104
- package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +106 -84
- package/src/resources/extensions/gsd/tests/doctor-roadmap-summary-atomicity.test.ts +60 -54
- package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +93 -72
- package/src/resources/extensions/gsd/tests/doctor.test.ts +134 -104
- package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +131 -123
- package/src/resources/extensions/gsd/tests/exit-command.test.ts +24 -20
- package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +57 -48
- package/src/resources/extensions/gsd/tests/files-loadfile-eisdir.test.ts +7 -5
- package/src/resources/extensions/gsd/tests/flag-file-db.test.ts +42 -30
- package/src/resources/extensions/gsd/tests/freeform-decisions.test.ts +206 -198
- package/src/resources/extensions/gsd/tests/git-locale.test.ts +27 -13
- package/src/resources/extensions/gsd/tests/git-service.test.ts +388 -285
- package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +39 -31
- package/src/resources/extensions/gsd/tests/graph-operations.test.ts +69 -63
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +264 -255
- package/src/resources/extensions/gsd/tests/gsd-inspect.test.ts +119 -108
- package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +103 -81
- package/src/resources/extensions/gsd/tests/gsd-tools.test.ts +262 -229
- package/src/resources/extensions/gsd/tests/headless-answers.test.ts +13 -13
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +37 -29
- package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +102 -81
- package/src/resources/extensions/gsd/tests/init-wizard.test.ts +18 -16
- package/src/resources/extensions/gsd/tests/integration-edge.test.ts +46 -41
- package/src/resources/extensions/gsd/tests/integration-lifecycle.test.ts +53 -42
- package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +91 -75
- package/src/resources/extensions/gsd/tests/integration-proof.test.ts +18 -18
- package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +194 -150
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +125 -101
- package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +54 -45
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +93 -80
- package/src/resources/extensions/gsd/tests/migrate-command.test.ts +66 -57
- package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +93 -83
- package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +170 -161
- package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +141 -125
- package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +131 -107
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +96 -87
- package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +164 -125
- package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +94 -81
- package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +36 -35
- package/src/resources/extensions/gsd/tests/overrides.test.ts +106 -99
- package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +47 -40
- package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +28 -25
- package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +83 -66
- package/src/resources/extensions/gsd/tests/park-edge-cases.test.ts +77 -54
- package/src/resources/extensions/gsd/tests/park-milestone.test.ts +115 -68
- package/src/resources/extensions/gsd/tests/parsers.test.ts +611 -546
- package/src/resources/extensions/gsd/tests/paths.test.ts +87 -72
- package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +117 -77
- package/src/resources/extensions/gsd/tests/prompt-db.test.ts +56 -56
- package/src/resources/extensions/gsd/tests/queue-draft-detection.test.ts +119 -93
- package/src/resources/extensions/gsd/tests/queue-order.test.ts +82 -70
- package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +55 -42
- package/src/resources/extensions/gsd/tests/quick-branch-lifecycle.test.ts +73 -45
- package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +38 -28
- package/src/resources/extensions/gsd/tests/replan-slice.test.ts +80 -73
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +74 -71
- package/src/resources/extensions/gsd/tests/requirements.test.ts +75 -70
- package/src/resources/extensions/gsd/tests/retry-state-reset.test.ts +66 -44
- package/src/resources/extensions/gsd/tests/roadmap-parse-regression.test.ts +181 -114
- package/src/resources/extensions/gsd/tests/rule-registry.test.ts +65 -63
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +128 -66
- package/src/resources/extensions/gsd/tests/session-lock-multipath.test.ts +25 -18
- package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +44 -37
- package/src/resources/extensions/gsd/tests/shared-wal.test.ts +26 -19
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +8 -6
- package/src/resources/extensions/gsd/tests/symlink-numbered-variants.test.ts +28 -22
- package/src/resources/extensions/gsd/tests/token-savings.test.ts +56 -54
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +25 -23
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +11 -9
- package/src/resources/extensions/gsd/tests/unique-milestone-ids.test.ts +82 -66
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +47 -46
- package/src/resources/extensions/gsd/tests/visualizer-critical-path.test.ts +22 -20
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +86 -84
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +43 -41
- package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +96 -94
- package/src/resources/extensions/gsd/tests/windows-path-normalization.test.ts +13 -11
- package/src/resources/extensions/gsd/tests/worker-registry.test.ts +29 -27
- package/src/resources/extensions/gsd/tests/workflow-templates.test.ts +52 -50
- package/src/resources/extensions/gsd/tests/worktree-bugfix.test.ts +13 -10
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +18 -14
- package/src/resources/extensions/gsd/tests/worktree-db.test.ts +39 -38
- package/src/resources/extensions/gsd/tests/worktree-e2e.test.ts +21 -17
- package/src/resources/extensions/gsd/tests/worktree-health.test.ts +30 -25
- package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +37 -30
- package/src/resources/extensions/gsd/tests/worktree-symlink-removal.test.ts +22 -15
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +66 -59
- package/src/resources/extensions/gsd/tests/worktree.test.ts +50 -44
- package/dist/web/standalone/.next/static/chunks/app/page-b9367c5ae13b99c6.js +0 -1
- package/dist/web/standalone/.next/static/chunks/main-app-fdab67f7802d7832.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-459824ffb8c323dd.js +0 -1
- package/src/resources/extensions/gsd/tests/quick-auto-guard.test.ts +0 -100
- package/src/resources/extensions/gsd/tests/sqlite-unavailable-gate.test.ts +0 -63
- /package/dist/web/standalone/.next/static/{-zps1Q9mQmioAKLcQiCr8 → mgkxN0mGP6gSUmGPEzbk_}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{-zps1Q9mQmioAKLcQiCr8 → mgkxN0mGP6gSUmGPEzbk_}/_ssgManifest.js +0 -0
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { describe, test } from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
1
|
// ensureDbOpen — Tests that the lazy DB opener creates + migrates the database
|
|
4
2
|
// when .gsd/ exists with Markdown content but no gsd.db file.
|
|
5
3
|
//
|
|
@@ -7,11 +5,14 @@ import assert from 'node:assert/strict';
|
|
|
7
5
|
// "GSD database is not available" because ensureDbOpen only opened
|
|
8
6
|
// existing DB files but never created them.
|
|
9
7
|
|
|
8
|
+
import { createTestContext } from './test-helpers.ts';
|
|
10
9
|
import * as path from 'node:path';
|
|
11
10
|
import * as os from 'node:os';
|
|
12
11
|
import * as fs from 'node:fs';
|
|
13
12
|
import { closeDatabase, isDbAvailable, getDecisionById } from '../gsd-db.ts';
|
|
14
13
|
|
|
14
|
+
const { assertEq, assertTrue, report } = createTestContext();
|
|
15
|
+
|
|
15
16
|
function makeTmpDir(): string {
|
|
16
17
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-ensure-db-'));
|
|
17
18
|
return dir;
|
|
@@ -27,134 +28,141 @@ function cleanupDir(dir: string): void {
|
|
|
27
28
|
// ensureDbOpen creates DB + migrates when .gsd/ has Markdown
|
|
28
29
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
29
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
|
-
} finally {
|
|
74
|
-
process.cwd = origCwd;
|
|
75
|
-
closeDatabase();
|
|
76
|
-
cleanupDir(tmpDir);
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
81
|
-
// ensureDbOpen returns false when no .gsd/ exists
|
|
82
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
83
|
-
|
|
84
|
-
test('ensureDbOpen: no .gsd/ returns false', async () => {
|
|
85
|
-
const tmpDir = makeTmpDir();
|
|
86
|
-
// No .gsd/ directory at all
|
|
87
|
-
|
|
88
|
-
try { closeDatabase(); } catch { /* ok */ }
|
|
89
|
-
const origCwd = process.cwd;
|
|
90
|
-
process.cwd = () => tmpDir;
|
|
91
|
-
|
|
92
|
-
try {
|
|
93
|
-
const { ensureDbOpen } = await import('../bootstrap/dynamic-tools.ts');
|
|
94
|
-
const result = await ensureDbOpen();
|
|
95
|
-
assert.ok(result === false, 'ensureDbOpen should return false when no .gsd/ exists');
|
|
96
|
-
assert.ok(!isDbAvailable(), 'DB should not be available');
|
|
97
|
-
} finally {
|
|
98
|
-
process.cwd = origCwd;
|
|
99
|
-
cleanupDir(tmpDir);
|
|
31
|
+
console.log('\n── ensureDbOpen: creates DB from Markdown ──');
|
|
32
|
+
|
|
33
|
+
{
|
|
34
|
+
const tmpDir = makeTmpDir();
|
|
35
|
+
const gsdDir = path.join(tmpDir, '.gsd');
|
|
36
|
+
fs.mkdirSync(gsdDir, { recursive: true });
|
|
37
|
+
|
|
38
|
+
// Write a minimal DECISIONS.md so migration has content
|
|
39
|
+
const decisionsContent = `# Decisions
|
|
40
|
+
|
|
41
|
+
| # | When | Scope | Decision | Choice | Rationale | Revisable |
|
|
42
|
+
|---|------|-------|----------|--------|-----------|-----------|
|
|
43
|
+
| D001 | M001 | architecture | Use SQLite | SQLite | Sync API | Yes |
|
|
44
|
+
`;
|
|
45
|
+
fs.writeFileSync(path.join(gsdDir, 'DECISIONS.md'), decisionsContent);
|
|
46
|
+
|
|
47
|
+
// Verify no DB file exists yet
|
|
48
|
+
const dbPath = path.join(gsdDir, 'gsd.db');
|
|
49
|
+
assertTrue(!fs.existsSync(dbPath), 'DB file should not exist before ensureDbOpen');
|
|
50
|
+
|
|
51
|
+
// Close any previously open DB
|
|
52
|
+
try { closeDatabase(); } catch { /* ok */ }
|
|
53
|
+
|
|
54
|
+
// Override process.cwd to point at tmpDir for ensureDbOpen
|
|
55
|
+
const origCwd = process.cwd;
|
|
56
|
+
process.cwd = () => tmpDir;
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
// Dynamic import to get the freshest version
|
|
60
|
+
const { ensureDbOpen } = await import('../bootstrap/dynamic-tools.ts');
|
|
61
|
+
|
|
62
|
+
const result = await ensureDbOpen();
|
|
63
|
+
|
|
64
|
+
assertTrue(result === true, 'ensureDbOpen should return true when .gsd/ has Markdown');
|
|
65
|
+
assertTrue(fs.existsSync(dbPath), 'DB file should be created after ensureDbOpen');
|
|
66
|
+
assertTrue(isDbAvailable(), 'DB should be available after ensureDbOpen');
|
|
67
|
+
|
|
68
|
+
// Verify that Markdown migration actually ran
|
|
69
|
+
const decision = getDecisionById('D001');
|
|
70
|
+
assertTrue(decision !== null, 'D001 should be migrated from DECISIONS.md');
|
|
71
|
+
if (decision) {
|
|
72
|
+
assertEq(decision.scope, 'architecture', 'Migrated decision scope should match');
|
|
73
|
+
assertEq(decision.choice, 'SQLite', 'Migrated decision choice should match');
|
|
100
74
|
}
|
|
101
|
-
}
|
|
75
|
+
} finally {
|
|
76
|
+
process.cwd = origCwd;
|
|
77
|
+
closeDatabase();
|
|
78
|
+
cleanupDir(tmpDir);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
83
|
+
// ensureDbOpen returns false when no .gsd/ exists
|
|
84
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
85
|
+
|
|
86
|
+
console.log('\n── ensureDbOpen: no .gsd/ returns false ──');
|
|
102
87
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
//
|
|
88
|
+
{
|
|
89
|
+
const tmpDir = makeTmpDir();
|
|
90
|
+
// No .gsd/ directory at all
|
|
106
91
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
fs.mkdirSync(gsdDir, { recursive: true });
|
|
92
|
+
try { closeDatabase(); } catch { /* ok */ }
|
|
93
|
+
const origCwd = process.cwd;
|
|
94
|
+
process.cwd = () => tmpDir;
|
|
111
95
|
|
|
112
|
-
|
|
113
|
-
const
|
|
114
|
-
const
|
|
115
|
-
|
|
96
|
+
try {
|
|
97
|
+
const { ensureDbOpen } = await import('../bootstrap/dynamic-tools.ts');
|
|
98
|
+
const result = await ensureDbOpen();
|
|
99
|
+
assertTrue(result === false, 'ensureDbOpen should return false when no .gsd/ exists');
|
|
100
|
+
assertTrue(!isDbAvailable(), 'DB should not be available');
|
|
101
|
+
} finally {
|
|
102
|
+
process.cwd = origCwd;
|
|
103
|
+
cleanupDir(tmpDir);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
108
|
+
// ensureDbOpen opens existing DB without re-migration
|
|
109
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
110
|
+
|
|
111
|
+
console.log('\n── ensureDbOpen: opens existing DB ──');
|
|
112
|
+
|
|
113
|
+
{
|
|
114
|
+
const tmpDir = makeTmpDir();
|
|
115
|
+
const gsdDir = path.join(tmpDir, '.gsd');
|
|
116
|
+
fs.mkdirSync(gsdDir, { recursive: true });
|
|
117
|
+
|
|
118
|
+
// Create a DB file first
|
|
119
|
+
const dbPath = path.join(gsdDir, 'gsd.db');
|
|
120
|
+
const { openDatabase } = await import('../gsd-db.ts');
|
|
121
|
+
openDatabase(dbPath);
|
|
122
|
+
closeDatabase();
|
|
123
|
+
|
|
124
|
+
assertTrue(fs.existsSync(dbPath), 'DB file should exist from manual create');
|
|
125
|
+
|
|
126
|
+
const origCwd = process.cwd;
|
|
127
|
+
process.cwd = () => tmpDir;
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
const { ensureDbOpen } = await import('../bootstrap/dynamic-tools.ts');
|
|
131
|
+
const result = await ensureDbOpen();
|
|
132
|
+
assertTrue(result === true, 'ensureDbOpen should open existing DB');
|
|
133
|
+
assertTrue(isDbAvailable(), 'DB should be available');
|
|
134
|
+
} finally {
|
|
135
|
+
process.cwd = origCwd;
|
|
116
136
|
closeDatabase();
|
|
137
|
+
cleanupDir(tmpDir);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
117
140
|
|
|
118
|
-
|
|
141
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
142
|
+
// ensureDbOpen returns false for empty .gsd/ (no Markdown, no DB)
|
|
143
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
119
144
|
|
|
120
|
-
|
|
121
|
-
process.cwd = () => tmpDir;
|
|
145
|
+
console.log('\n── ensureDbOpen: empty .gsd/ returns false ──');
|
|
122
146
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
assert.ok(isDbAvailable(), 'DB should be available');
|
|
128
|
-
} finally {
|
|
129
|
-
process.cwd = origCwd;
|
|
130
|
-
closeDatabase();
|
|
131
|
-
cleanupDir(tmpDir);
|
|
132
|
-
}
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
136
|
-
// ensureDbOpen returns false for empty .gsd/ (no Markdown, no DB)
|
|
137
|
-
// ═══════════════════════════════════════════════════════════════════════════
|
|
138
|
-
|
|
139
|
-
test('ensureDbOpen: empty .gsd/ returns false', async () => {
|
|
140
|
-
const tmpDir = makeTmpDir();
|
|
141
|
-
fs.mkdirSync(path.join(tmpDir, '.gsd'), { recursive: true });
|
|
142
|
-
// .gsd/ exists but no DECISIONS.md, REQUIREMENTS.md, or milestones/
|
|
143
|
-
|
|
144
|
-
try { closeDatabase(); } catch { /* ok */ }
|
|
145
|
-
const origCwd = process.cwd;
|
|
146
|
-
process.cwd = () => tmpDir;
|
|
147
|
-
|
|
148
|
-
try {
|
|
149
|
-
const { ensureDbOpen } = await import('../bootstrap/dynamic-tools.ts');
|
|
150
|
-
const result = await ensureDbOpen();
|
|
151
|
-
assert.ok(result === false, 'ensureDbOpen should return false for empty .gsd/');
|
|
152
|
-
} finally {
|
|
153
|
-
process.cwd = origCwd;
|
|
154
|
-
cleanupDir(tmpDir);
|
|
155
|
-
}
|
|
156
|
-
});
|
|
147
|
+
{
|
|
148
|
+
const tmpDir = makeTmpDir();
|
|
149
|
+
fs.mkdirSync(path.join(tmpDir, '.gsd'), { recursive: true });
|
|
150
|
+
// .gsd/ exists but no DECISIONS.md, REQUIREMENTS.md, or milestones/
|
|
157
151
|
|
|
158
|
-
|
|
152
|
+
try { closeDatabase(); } catch { /* ok */ }
|
|
153
|
+
const origCwd = process.cwd;
|
|
154
|
+
process.cwd = () => tmpDir;
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
const { ensureDbOpen } = await import('../bootstrap/dynamic-tools.ts');
|
|
158
|
+
const result = await ensureDbOpen();
|
|
159
|
+
assertTrue(result === false, 'ensureDbOpen should return false for empty .gsd/');
|
|
160
|
+
} finally {
|
|
161
|
+
process.cwd = origCwd;
|
|
162
|
+
cleanupDir(tmpDir);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
159
167
|
|
|
160
|
-
|
|
168
|
+
report();
|
|
@@ -3,7 +3,7 @@ import assert from "node:assert/strict";
|
|
|
3
3
|
|
|
4
4
|
import { registerExitCommand } from "../exit-command.ts";
|
|
5
5
|
|
|
6
|
-
test("/exit requests graceful shutdown instead of process.exit", async (
|
|
6
|
+
test("/exit requests graceful shutdown instead of process.exit", async () => {
|
|
7
7
|
const commands = new Map<
|
|
8
8
|
string,
|
|
9
9
|
{
|
|
@@ -35,13 +35,15 @@ test("/exit requests graceful shutdown instead of process.exit", async (t) => {
|
|
|
35
35
|
throw new Error(`process.exit should not be called: ${code ?? "undefined"}`);
|
|
36
36
|
}) as typeof process.exit;
|
|
37
37
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
44
|
-
}
|
|
38
|
+
try {
|
|
39
|
+
await exit.handler("", {
|
|
40
|
+
async shutdown() {
|
|
41
|
+
shutdownCalls += 1;
|
|
42
|
+
},
|
|
43
|
+
});
|
|
44
|
+
} finally {
|
|
45
|
+
process.exit = originalExit;
|
|
46
|
+
}
|
|
45
47
|
|
|
46
48
|
assert.equal(stopAutoCalls, 1, "handler should stop auto-mode exactly once before shutdown");
|
|
47
49
|
assert.equal(shutdownCalls, 1, "handler should request graceful shutdown exactly once");
|
|
@@ -49,7 +51,7 @@ test("/exit requests graceful shutdown instead of process.exit", async (t) => {
|
|
|
49
51
|
|
|
50
52
|
// ─── #1839 regression: ESM cache mismatch must not crash exit ────────────────
|
|
51
53
|
|
|
52
|
-
test("/exit still shuts down gracefully when stopAuto throws (ESM module cache mismatch)", async (
|
|
54
|
+
test("/exit still shuts down gracefully when stopAuto throws (ESM module cache mismatch)", async () => {
|
|
53
55
|
const commands = new Map<string, { description?: string; handler: (args: string, ctx: any) => Promise<void> }>();
|
|
54
56
|
|
|
55
57
|
const pi = {
|
|
@@ -78,18 +80,20 @@ test("/exit still shuts down gracefully when stopAuto throws (ESM module cache m
|
|
|
78
80
|
throw new Error(`process.exit should not be called: ${code ?? "undefined"}`);
|
|
79
81
|
}) as typeof process.exit;
|
|
80
82
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
shutdownCalls += 1;
|
|
86
|
-
},
|
|
87
|
-
ui: {
|
|
88
|
-
notify(msg: string, level: string) {
|
|
89
|
-
notifications.push({ msg, level });
|
|
83
|
+
try {
|
|
84
|
+
await exit.handler("", {
|
|
85
|
+
async shutdown() {
|
|
86
|
+
shutdownCalls += 1;
|
|
90
87
|
},
|
|
91
|
-
|
|
92
|
-
|
|
88
|
+
ui: {
|
|
89
|
+
notify(msg: string, level: string) {
|
|
90
|
+
notifications.push({ msg, level });
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
} finally {
|
|
95
|
+
process.exit = originalExit;
|
|
96
|
+
}
|
|
93
97
|
|
|
94
98
|
assert.equal(shutdownCalls, 1, "shutdown must still be called even when stopAuto throws");
|
|
95
99
|
assert.equal(notifications.length, 1, "should emit exactly one warning notification");
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
import { describe, test } from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
1
|
/**
|
|
4
2
|
* feature-branch-lifecycle.test.ts — Integration tests for the feature-branch workflow.
|
|
5
3
|
*
|
|
@@ -31,6 +29,10 @@ import { captureIntegrationBranch, getSliceBranchName } from "../worktree.ts";
|
|
|
31
29
|
import { writeIntegrationBranch, readIntegrationBranch } from "../git-service.ts";
|
|
32
30
|
import { nextMilestoneId, generateMilestoneSuffix } from "../guided-flow.ts";
|
|
33
31
|
|
|
32
|
+
import { createTestContext } from "./test-helpers.ts";
|
|
33
|
+
|
|
34
|
+
const { assertEq, assertTrue, assertMatch, report } = createTestContext();
|
|
35
|
+
|
|
34
36
|
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
35
37
|
|
|
36
38
|
function run(cmd: string, cwd: string): string {
|
|
@@ -135,7 +137,7 @@ function addSliceToMilestone(
|
|
|
135
137
|
|
|
136
138
|
// ─── Tests ──────────────────────────────────────────────────────────────────
|
|
137
139
|
|
|
138
|
-
|
|
140
|
+
async function main(): Promise<void> {
|
|
139
141
|
const savedCwd = process.cwd();
|
|
140
142
|
const tempDirs: string[] = [];
|
|
141
143
|
|
|
@@ -152,13 +154,14 @@ describe('feature-branch-lifecycle-integration', async () => {
|
|
|
152
154
|
// Start on f-new-shiny-thing with uncommitted changes, create
|
|
153
155
|
// worktree, add slices, merge back. Assert main is untouched.
|
|
154
156
|
// ================================================================
|
|
155
|
-
|
|
157
|
+
console.log("\n=== Feature-branch lifecycle with unique milestone IDs ===");
|
|
158
|
+
{
|
|
156
159
|
const featureBranch = "f-new-shiny-thing";
|
|
157
160
|
const repo = fresh(featureBranch);
|
|
158
161
|
|
|
159
162
|
// Generate a unique milestone ID (M001-xxxxxx format)
|
|
160
163
|
const milestoneId = nextMilestoneId([], true);
|
|
161
|
-
|
|
164
|
+
assertMatch(milestoneId, /^M001-[a-z0-9]{6}$/, "unique milestone ID format");
|
|
162
165
|
|
|
163
166
|
// Snapshot main before anything happens
|
|
164
167
|
const mainShaBefore = headSha(repo, "main");
|
|
@@ -171,8 +174,8 @@ describe('feature-branch-lifecycle-integration', async () => {
|
|
|
171
174
|
|
|
172
175
|
// Verify files are uncommitted
|
|
173
176
|
const statusBefore = run("git status --short", repo);
|
|
174
|
-
|
|
175
|
-
|
|
177
|
+
assertTrue(statusBefore.includes("wip-config.ts"), "wip-config.ts is uncommitted");
|
|
178
|
+
assertTrue(statusBefore.includes("wip-types.ts"), "wip-types.ts is uncommitted");
|
|
176
179
|
|
|
177
180
|
// ── Simulate what startAuto does: commit dirty state, capture integration branch ──
|
|
178
181
|
// startAuto bootstraps .gsd/ which commits .gsd/ files. It also calls
|
|
@@ -195,7 +198,7 @@ describe('feature-branch-lifecycle-integration', async () => {
|
|
|
195
198
|
|
|
196
199
|
// Verify integration branch recorded
|
|
197
200
|
const recorded = readIntegrationBranch(repo, milestoneId);
|
|
198
|
-
|
|
201
|
+
assertEq(recorded, featureBranch, "integration branch recorded as feature branch");
|
|
199
202
|
|
|
200
203
|
// Snapshot feature branch SHA after metadata commit (HEAD may have advanced)
|
|
201
204
|
const featureShaBeforeWorktree = headSha(repo, featureBranch);
|
|
@@ -203,28 +206,28 @@ describe('feature-branch-lifecycle-integration', async () => {
|
|
|
203
206
|
// ── Create the auto-worktree ──
|
|
204
207
|
const wtPath = createAutoWorktree(repo, milestoneId);
|
|
205
208
|
tempDirs.push(wtPath);
|
|
206
|
-
|
|
209
|
+
assertTrue(existsSync(wtPath), "worktree directory created");
|
|
207
210
|
|
|
208
211
|
// Worktree should be on milestone/<unique-id> branch
|
|
209
212
|
const wtBranch = run("git branch --show-current", wtPath);
|
|
210
|
-
|
|
213
|
+
assertEq(wtBranch, `milestone/${milestoneId}`, "worktree is on milestone branch");
|
|
211
214
|
|
|
212
215
|
// Milestone branch should be rooted at the feature branch, not main
|
|
213
216
|
const milestoneBranchBase = headSha(repo, `milestone/${milestoneId}`);
|
|
214
|
-
|
|
217
|
+
assertEq(
|
|
215
218
|
milestoneBranchBase,
|
|
216
219
|
featureShaBeforeWorktree,
|
|
217
220
|
"milestone branch starts from feature branch HEAD",
|
|
218
221
|
);
|
|
219
222
|
|
|
220
223
|
// Feature-branch-only file should be in the worktree
|
|
221
|
-
|
|
224
|
+
assertTrue(
|
|
222
225
|
existsSync(join(wtPath, "feature-setup.ts")),
|
|
223
226
|
"feature branch file (feature-setup.ts) exists in worktree",
|
|
224
227
|
);
|
|
225
228
|
|
|
226
229
|
// Main should be completely untouched at this point
|
|
227
|
-
|
|
230
|
+
assertEq(headSha(repo, "main"), mainShaBefore, "main SHA unchanged after worktree creation");
|
|
228
231
|
|
|
229
232
|
// ── Do work in slices ──
|
|
230
233
|
addSliceToMilestone(wtPath, milestoneId, "S01", "Auth module", [
|
|
@@ -247,62 +250,62 @@ describe('feature-branch-lifecycle-integration', async () => {
|
|
|
247
250
|
|
|
248
251
|
// ── Assert: feature branch received the merge ──
|
|
249
252
|
const currentBranch = run("git branch --show-current", repo);
|
|
250
|
-
|
|
253
|
+
assertEq(currentBranch, featureBranch, "repo is on feature branch after merge");
|
|
251
254
|
|
|
252
255
|
// Exactly one new commit on feature branch (the squash merge)
|
|
253
256
|
const featureLog = run(`git log --oneline ${featureBranch}`, repo);
|
|
254
|
-
|
|
257
|
+
assertTrue(
|
|
255
258
|
featureLog.includes(`feat(${milestoneId})`),
|
|
256
259
|
"feature branch has milestone merge commit",
|
|
257
260
|
);
|
|
258
261
|
|
|
259
262
|
// Slice files are on the feature branch
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
+
assertTrue(existsSync(join(repo, "auth.ts")), "auth.ts on feature branch");
|
|
264
|
+
assertTrue(existsSync(join(repo, "dashboard.ts")), "dashboard.ts on feature branch");
|
|
265
|
+
assertTrue(existsSync(join(repo, "auth-utils.ts")), "auth-utils.ts on feature branch");
|
|
263
266
|
|
|
264
267
|
// Original feature branch file still present
|
|
265
|
-
|
|
268
|
+
assertTrue(existsSync(join(repo, "feature-setup.ts")), "feature-setup.ts still on feature branch");
|
|
266
269
|
|
|
267
270
|
// Commit message is well-formed
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
271
|
+
assertTrue(result.commitMessage.includes("New shiny feature"), "commit message has milestone title");
|
|
272
|
+
assertTrue(result.commitMessage.includes("S01: Auth module"), "commit message lists S01");
|
|
273
|
+
assertTrue(result.commitMessage.includes("S02: Dashboard"), "commit message lists S02");
|
|
274
|
+
assertTrue(
|
|
272
275
|
result.commitMessage.includes(`milestone/${milestoneId}`),
|
|
273
276
|
"commit message references milestone branch with unique ID",
|
|
274
277
|
);
|
|
275
278
|
|
|
276
279
|
// ── Assert: main is COMPLETELY untouched ──
|
|
277
|
-
|
|
278
|
-
|
|
280
|
+
assertEq(headSha(repo, "main"), mainShaBefore, "main SHA unchanged after merge");
|
|
281
|
+
assertEq(commitCount(repo, "main"), mainCommitsBefore, "main commit count unchanged");
|
|
279
282
|
|
|
280
283
|
// Main should NOT have any of the milestone files
|
|
281
284
|
run("git checkout main", repo);
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
+
assertTrue(!existsSync(join(repo, "auth.ts")), "auth.ts NOT on main");
|
|
286
|
+
assertTrue(!existsSync(join(repo, "dashboard.ts")), "dashboard.ts NOT on main");
|
|
287
|
+
assertTrue(!existsSync(join(repo, "feature-setup.ts")), "feature-setup.ts NOT on main");
|
|
285
288
|
run(`git checkout ${featureBranch}`, repo);
|
|
286
289
|
|
|
287
290
|
// ── Assert: worktree cleaned up ──
|
|
288
291
|
const worktreeDir = join(repo, ".gsd", "worktrees", milestoneId);
|
|
289
|
-
|
|
292
|
+
assertTrue(!existsSync(worktreeDir), "worktree directory removed");
|
|
290
293
|
|
|
291
294
|
// Milestone branch deleted
|
|
292
|
-
|
|
295
|
+
assertTrue(
|
|
293
296
|
!branchExists(repo, `milestone/${milestoneId}`),
|
|
294
297
|
"milestone branch deleted after merge",
|
|
295
298
|
);
|
|
296
299
|
|
|
297
300
|
// Only expected branches remain
|
|
298
301
|
const branches = allBranches(repo);
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
+
assertTrue(branches.includes("main"), "main branch exists");
|
|
303
|
+
assertTrue(branches.includes(featureBranch), "feature branch exists");
|
|
304
|
+
assertTrue(
|
|
302
305
|
!branches.some(b => b.startsWith("milestone/")),
|
|
303
306
|
"no milestone branches remain",
|
|
304
307
|
);
|
|
305
|
-
}
|
|
308
|
+
}
|
|
306
309
|
|
|
307
310
|
// ================================================================
|
|
308
311
|
// Test 2: Uncommitted .gsd/ planning files are available in worktree
|
|
@@ -311,7 +314,8 @@ describe('feature-branch-lifecycle-integration', async () => {
|
|
|
311
314
|
// Planning artifacts should be carried into the worktree even if
|
|
312
315
|
// they weren't committed on the feature branch.
|
|
313
316
|
// ================================================================
|
|
314
|
-
|
|
317
|
+
console.log("\n=== Untracked planning files copied to worktree ===");
|
|
318
|
+
{
|
|
315
319
|
const featureBranch = "f-planning-test";
|
|
316
320
|
const repo = fresh(featureBranch);
|
|
317
321
|
const milestoneId = nextMilestoneId([], true);
|
|
@@ -330,7 +334,7 @@ describe('feature-branch-lifecycle-integration', async () => {
|
|
|
330
334
|
writeFileSync(join(repo, ".gsd", "DECISIONS.md"), "# Decisions\n\n## D001\nTest decision.\n");
|
|
331
335
|
|
|
332
336
|
// These files are untracked
|
|
333
|
-
|
|
337
|
+
assertTrue(run("git status --short", repo).length > 0, "repo has untracked files");
|
|
334
338
|
|
|
335
339
|
// Record integration branch and create worktree
|
|
336
340
|
writeIntegrationBranch(repo, milestoneId, featureBranch);
|
|
@@ -340,11 +344,11 @@ describe('feature-branch-lifecycle-integration', async () => {
|
|
|
340
344
|
// With external state, worktree .gsd is a symlink to shared state.
|
|
341
345
|
// Verify symlink was created (planning files are shared, not copied).
|
|
342
346
|
const wtGsd = join(wtPath, ".gsd");
|
|
343
|
-
|
|
347
|
+
assertTrue(existsSync(wtGsd), "worktree .gsd exists (symlink or dir)");
|
|
344
348
|
|
|
345
349
|
// Clean up: chdir back before teardown
|
|
346
350
|
process.chdir(savedCwd);
|
|
347
|
-
}
|
|
351
|
+
}
|
|
348
352
|
|
|
349
353
|
// ================================================================
|
|
350
354
|
// Test 3: Multiple milestones on the same feature branch
|
|
@@ -352,7 +356,8 @@ describe('feature-branch-lifecycle-integration', async () => {
|
|
|
352
356
|
// Proves that unique IDs prevent collision when running successive
|
|
353
357
|
// milestones, and each merge lands on the feature branch.
|
|
354
358
|
// ================================================================
|
|
355
|
-
|
|
359
|
+
console.log("\n=== Multiple unique milestones on same feature branch ===");
|
|
360
|
+
{
|
|
356
361
|
const featureBranch = "f-multi-milestone";
|
|
357
362
|
const repo = fresh(featureBranch);
|
|
358
363
|
|
|
@@ -372,12 +377,12 @@ describe('feature-branch-lifecycle-integration', async () => {
|
|
|
372
377
|
mergeMilestoneToMain(repo, mid1, makeRoadmap(mid1, "First", [{ id: "S01", title: "First milestone work" }]));
|
|
373
378
|
process.chdir(savedCwd);
|
|
374
379
|
|
|
375
|
-
|
|
380
|
+
assertTrue(existsSync(join(repo, "m1-feature.ts")), "m1 file on feature branch");
|
|
376
381
|
|
|
377
382
|
// Second milestone — different unique ID
|
|
378
383
|
const mid2 = nextMilestoneId([mid1], true);
|
|
379
|
-
|
|
380
|
-
|
|
384
|
+
assertTrue(mid1 !== mid2, "second milestone has different ID");
|
|
385
|
+
assertMatch(mid2, /^M002-[a-z0-9]{6}$/, "second milestone is M002-xxxxxx");
|
|
381
386
|
|
|
382
387
|
mkdirSync(join(repo, ".gsd", "milestones", mid2), { recursive: true });
|
|
383
388
|
writeIntegrationBranch(repo, mid2, featureBranch);
|
|
@@ -392,19 +397,19 @@ describe('feature-branch-lifecycle-integration', async () => {
|
|
|
392
397
|
process.chdir(savedCwd);
|
|
393
398
|
|
|
394
399
|
// Both milestone files on feature branch
|
|
395
|
-
|
|
396
|
-
|
|
400
|
+
assertTrue(existsSync(join(repo, "m1-feature.ts")), "m1 file still on feature branch");
|
|
401
|
+
assertTrue(existsSync(join(repo, "m2-feature.ts")), "m2 file on feature branch");
|
|
397
402
|
|
|
398
403
|
// Main completely untouched
|
|
399
|
-
|
|
404
|
+
assertEq(headSha(repo, "main"), mainShaBefore, "main unchanged after two milestones");
|
|
400
405
|
|
|
401
406
|
// No milestone branches remain
|
|
402
407
|
const branches = allBranches(repo);
|
|
403
|
-
|
|
408
|
+
assertTrue(
|
|
404
409
|
!branches.some(b => b.startsWith("milestone/")),
|
|
405
410
|
"no milestone branches remain after two milestones",
|
|
406
411
|
);
|
|
407
|
-
}
|
|
412
|
+
}
|
|
408
413
|
|
|
409
414
|
} finally {
|
|
410
415
|
process.chdir(savedCwd);
|
|
@@ -412,4 +417,8 @@ describe('feature-branch-lifecycle-integration', async () => {
|
|
|
412
417
|
try { rmSync(d, { recursive: true, force: true }); } catch { /* ignore */ }
|
|
413
418
|
}
|
|
414
419
|
}
|
|
415
|
-
|
|
420
|
+
|
|
421
|
+
report();
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
main();
|
|
@@ -6,13 +6,15 @@ import fs from "node:fs";
|
|
|
6
6
|
|
|
7
7
|
import { loadFile } from "../files.ts";
|
|
8
8
|
|
|
9
|
-
test("loadFile returns null for directory paths instead of throwing EISDIR", async (
|
|
9
|
+
test("loadFile returns null for directory paths instead of throwing EISDIR", async () => {
|
|
10
10
|
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), "gsd-loadfile-eisdir-"));
|
|
11
11
|
const dirPath = path.join(tmp, "tasks");
|
|
12
12
|
fs.mkdirSync(dirPath);
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
try {
|
|
15
|
+
const result = await loadFile(dirPath);
|
|
16
|
+
assert.equal(result, null);
|
|
17
|
+
} finally {
|
|
18
|
+
fs.rmSync(tmp, { recursive: true, force: true });
|
|
19
|
+
}
|
|
18
20
|
});
|