@opengsd/gsd-pi 1.5.0-dev.553dc518 → 1.5.0-dev.c4c8885f
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/ask-user-questions.js +1 -1
- package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +114 -36
- package/dist/resources/extensions/gsd/activity-log.js +1 -1
- package/dist/resources/extensions/gsd/auto/orchestrator.js +16 -10
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +14 -5
- package/dist/resources/extensions/gsd/auto-direct-dispatch.js +7 -2
- package/dist/resources/extensions/gsd/auto-dispatch.js +69 -17
- package/dist/resources/extensions/gsd/auto-model-selection.js +28 -1
- package/dist/resources/extensions/gsd/auto-post-unit.js +4 -3
- package/dist/resources/extensions/gsd/auto-prompts.js +29 -117
- package/dist/resources/extensions/gsd/auto-recovery.js +50 -6
- package/dist/resources/extensions/gsd/auto-start.js +54 -31
- package/dist/resources/extensions/gsd/auto-timeout-recovery.js +12 -2
- package/dist/resources/extensions/gsd/auto-timers.js +9 -2
- package/dist/resources/extensions/gsd/auto.js +97 -31
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +1 -1
- package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +67 -6
- package/dist/resources/extensions/gsd/codebase-generator.js +7 -1
- package/dist/resources/extensions/gsd/compat/compat-marker.js +44 -0
- package/dist/resources/extensions/gsd/config-overlay.js +7 -2
- package/dist/resources/extensions/gsd/db/engine.js +33 -0
- package/dist/resources/extensions/gsd/db/writers/cascades.js +20 -2
- package/dist/resources/extensions/gsd/db-adapter.js +30 -3
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +3 -3
- package/dist/resources/extensions/gsd/flat-phase-migration.js +18 -0
- package/dist/resources/extensions/gsd/gsd-db.js +12 -11
- package/dist/resources/extensions/gsd/guided-flow.js +16 -7
- package/dist/resources/extensions/gsd/journal.js +1 -1
- package/dist/resources/extensions/gsd/markdown-renderer.js +34 -91
- package/dist/resources/extensions/gsd/md-importer.js +89 -23
- package/dist/resources/extensions/gsd/metrics.js +2 -2
- package/dist/resources/extensions/gsd/migration-auto-check.js +30 -3
- package/dist/resources/extensions/gsd/paths.js +87 -52
- package/dist/resources/extensions/gsd/post-unit-hooks.js +27 -0
- package/dist/resources/extensions/gsd/preferences-models.js +66 -15
- package/dist/resources/extensions/gsd/preferences-skills.js +10 -16
- package/dist/resources/extensions/gsd/preferences-validation.js +66 -10
- package/dist/resources/extensions/gsd/preferences.js +20 -0
- package/dist/resources/extensions/gsd/rule-registry.js +106 -10
- package/dist/resources/extensions/gsd/skill-discovery.js +36 -23
- package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +1 -10
- package/dist/resources/extensions/gsd/state-reconciliation/drift/completion.js +3 -24
- package/dist/resources/extensions/gsd/state-reconciliation/index.js +10 -0
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +3 -5
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +16 -10
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +5 -0
- package/dist/resources/extensions/gsd/tools/reopen-task.js +13 -5
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +3 -8
- package/dist/resources/extensions/gsd/uat-dispatch.js +94 -0
- package/dist/resources/extensions/gsd/uok/audit-toggle.js +31 -2
- package/dist/resources/extensions/gsd/uok/audit.js +3 -0
- package/dist/resources/extensions/gsd/uok/gate-runner.js +22 -30
- package/dist/resources/extensions/gsd/uok/gitops.js +18 -15
- package/dist/resources/extensions/gsd/uok/kernel.js +120 -46
- package/dist/resources/extensions/gsd/uok/model-policy.js +17 -14
- package/dist/resources/extensions/gsd/workflow-event-ledger.js +1 -1
- package/dist/resources/extensions/gsd/workflow-logger.js +1 -1
- package/dist/resources/extensions/gsd/workflow-projections.js +9 -26
- package/dist/resources/extensions/gsd/workspace.js +5 -14
- package/dist/resources/extensions/shared/gsd-browser-cli.js +29 -0
- package/dist/resources/extensions/shared/interview-ui.js +62 -9
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
- 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 +1 -1
- 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/api/boot/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +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 +10 -10
- package/dist/web/standalone/.next/server/chunks/{1128.js → 1991.js} +1 -1
- package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/worktree-cli-create.d.ts +10 -0
- package/dist/worktree-cli-create.js +11 -0
- package/dist/worktree-cli-format.d.ts +12 -0
- package/dist/worktree-cli-format.js +38 -0
- package/dist/worktree-cli-plan.d.ts +16 -0
- package/dist/worktree-cli-plan.js +19 -0
- package/dist/worktree-cli-session.d.ts +13 -0
- package/dist/worktree-cli-session.js +18 -0
- package/dist/worktree-cli-status.d.ts +35 -0
- package/dist/worktree-cli-status.js +54 -0
- package/dist/worktree-cli.d.ts +1 -16
- package/dist/worktree-cli.js +28 -122
- package/integrations/hermes/open_gsd_hermes/gsd_client.py +1 -1
- package/integrations/hermes/pyproject.toml +1 -1
- package/package.json +1 -1
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/dist/orchestrator-agent.d.ts +21 -0
- package/packages/daemon/dist/orchestrator-agent.d.ts.map +1 -0
- package/packages/daemon/dist/orchestrator-agent.js +113 -0
- package/packages/daemon/dist/orchestrator-agent.js.map +1 -0
- package/packages/daemon/dist/orchestrator-tools.d.ts +12 -0
- package/packages/daemon/dist/orchestrator-tools.d.ts.map +1 -0
- package/packages/daemon/dist/orchestrator-tools.js +137 -0
- package/packages/daemon/dist/orchestrator-tools.js.map +1 -0
- package/packages/daemon/dist/orchestrator.d.ts +4 -56
- package/packages/daemon/dist/orchestrator.d.ts.map +1 -1
- package/packages/daemon/dist/orchestrator.js +13 -303
- package/packages/daemon/dist/orchestrator.js.map +1 -1
- package/packages/daemon/dist/orchestrator.test.js +11 -1
- package/packages/daemon/dist/orchestrator.test.js.map +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/package.json +4 -4
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.d.ts +9 -0
- package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/package-manager.js +74 -53
- package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts +6 -0
- package/packages/pi-coding-agent/dist/core/resource-loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/resource-loader.js +30 -2
- package/packages/pi-coding-agent/dist/core/resource-loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/skill-directories.d.ts +66 -0
- package/packages/pi-coding-agent/dist/core/skill-directories.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/skill-directories.js +138 -0
- package/packages/pi-coding-agent/dist/core/skill-directories.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/skills.d.ts +4 -1
- package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.js +4 -1
- package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +1 -0
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -0
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/package.json +2 -2
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- package/src/resources/extensions/ask-user-questions.ts +1 -1
- package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +146 -33
- package/src/resources/extensions/browser-tools/tests/gsd-browser-launch-config.test.mjs +25 -0
- package/src/resources/extensions/browser-tools/tests/managed-gsd-browser-tools.test.mjs +93 -0
- package/src/resources/extensions/gsd/activity-log.ts +1 -1
- package/src/resources/extensions/gsd/auto/orchestrator.ts +17 -11
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +14 -4
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +9 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +77 -16
- package/src/resources/extensions/gsd/auto-model-selection.ts +33 -1
- package/src/resources/extensions/gsd/auto-post-unit.ts +4 -2
- package/src/resources/extensions/gsd/auto-prompts.ts +53 -113
- package/src/resources/extensions/gsd/auto-recovery.ts +50 -6
- package/src/resources/extensions/gsd/auto-start.ts +58 -29
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +12 -2
- package/src/resources/extensions/gsd/auto-timers.ts +9 -2
- package/src/resources/extensions/gsd/auto.ts +103 -30
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +1 -1
- package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +66 -6
- package/src/resources/extensions/gsd/codebase-generator.ts +7 -1
- package/src/resources/extensions/gsd/compat/compat-marker.ts +47 -0
- package/src/resources/extensions/gsd/config-overlay.ts +7 -1
- package/src/resources/extensions/gsd/db/engine.ts +36 -0
- package/src/resources/extensions/gsd/db/writers/cascades.ts +23 -1
- package/src/resources/extensions/gsd/db-adapter.ts +29 -3
- package/src/resources/extensions/gsd/docs/preferences-reference.md +3 -3
- package/src/resources/extensions/gsd/flat-phase-migration.ts +20 -0
- package/src/resources/extensions/gsd/gsd-db.ts +20 -11
- package/src/resources/extensions/gsd/guided-flow.ts +19 -7
- package/src/resources/extensions/gsd/journal.ts +1 -1
- package/src/resources/extensions/gsd/markdown-renderer.ts +52 -103
- package/src/resources/extensions/gsd/md-importer.ts +93 -22
- package/src/resources/extensions/gsd/metrics.ts +2 -2
- package/src/resources/extensions/gsd/migration-auto-check.ts +32 -3
- package/src/resources/extensions/gsd/paths.ts +108 -51
- package/src/resources/extensions/gsd/post-unit-hooks.ts +32 -0
- package/src/resources/extensions/gsd/preferences-models.ts +74 -20
- package/src/resources/extensions/gsd/preferences-skills.ts +10 -16
- package/src/resources/extensions/gsd/preferences-types.ts +24 -1
- package/src/resources/extensions/gsd/preferences-validation.ts +77 -9
- package/src/resources/extensions/gsd/preferences.ts +20 -0
- package/src/resources/extensions/gsd/rule-registry.ts +111 -9
- package/src/resources/extensions/gsd/skill-discovery.ts +47 -23
- package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +1 -9
- package/src/resources/extensions/gsd/state-reconciliation/drift/completion.ts +2 -22
- package/src/resources/extensions/gsd/state-reconciliation/index.ts +14 -0
- package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +169 -1
- package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +41 -2
- package/src/resources/extensions/gsd/tests/auto-post-unit-artifact-diagnostic.test.ts +48 -1
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +57 -0
- package/src/resources/extensions/gsd/tests/auto-start-project-milestone-reconcile.test.ts +29 -1
- package/src/resources/extensions/gsd/tests/auto-timeout-recovery-supervisor-inflight-guard.test.ts +159 -0
- package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/compat-marker.test.ts +85 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +45 -0
- package/src/resources/extensions/gsd/tests/db-adapter.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/db-writable-probe.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +27 -0
- package/src/resources/extensions/gsd/tests/flat-phase-renderer.test.ts +22 -1
- package/src/resources/extensions/gsd/tests/flat-phase-validation-path.test.ts +72 -32
- package/src/resources/extensions/gsd/tests/import-completion-lost-update.test.ts +196 -0
- package/src/resources/extensions/gsd/tests/integration/paths.test.ts +88 -1
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +16 -15
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +91 -0
- package/src/resources/extensions/gsd/tests/milestone-closeout.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +0 -1
- package/src/resources/extensions/gsd/tests/post-unit-hooks.test.ts +269 -0
- package/src/resources/extensions/gsd/tests/post-unit-retry-on-orchestrator-bridge.test.ts +8 -1
- package/src/resources/extensions/gsd/tests/preferences.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/read-uat-gate-verdict.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/reassess-handler.test.ts +49 -1
- package/src/resources/extensions/gsd/tests/reopen-slice.test.ts +52 -0
- package/src/resources/extensions/gsd/tests/rule-registry.test.ts +95 -1
- package/src/resources/extensions/gsd/tests/sibling-model-fallbacks.test.ts +189 -0
- package/src/resources/extensions/gsd/tests/single-field-model-thinking.test.ts +156 -0
- package/src/resources/extensions/gsd/tests/skill-discovery.test.ts +46 -0
- package/src/resources/extensions/gsd/tests/skipped-validation-db-atomicity.test.ts +6 -1
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/supervisor-model-swap-inflight-guard.test.ts +181 -0
- package/src/resources/extensions/gsd/tests/supervisor-model-swap-thinking-clamp.test.ts +122 -0
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +93 -0
- package/src/resources/extensions/gsd/tests/uat-dispatch.test.ts +211 -0
- package/src/resources/extensions/gsd/tests/uok-gate-runner.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/uok-kernel-path.test.ts +226 -2
- package/src/resources/extensions/gsd/tests/uok-model-policy.test.ts +6 -0
- package/src/resources/extensions/gsd/tests/workflow-projections.test.ts +213 -2
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +9 -5
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +19 -8
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +5 -0
- package/src/resources/extensions/gsd/tools/reopen-task.ts +13 -5
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +13 -9
- package/src/resources/extensions/gsd/types.ts +23 -4
- package/src/resources/extensions/gsd/uat-dispatch.ts +176 -0
- package/src/resources/extensions/gsd/uok/audit-toggle.ts +32 -2
- package/src/resources/extensions/gsd/uok/audit.ts +2 -0
- package/src/resources/extensions/gsd/uok/gate-runner.ts +26 -36
- package/src/resources/extensions/gsd/uok/gitops.ts +21 -18
- package/src/resources/extensions/gsd/uok/kernel.ts +169 -48
- package/src/resources/extensions/gsd/uok/model-policy.ts +20 -17
- package/src/resources/extensions/gsd/workflow-event-ledger.ts +1 -1
- package/src/resources/extensions/gsd/workflow-logger.ts +1 -1
- package/src/resources/extensions/gsd/workflow-projections.ts +9 -24
- package/src/resources/extensions/gsd/workspace.ts +5 -21
- package/src/resources/extensions/shared/gsd-browser-cli.ts +29 -0
- package/src/resources/extensions/shared/interview-ui.ts +58 -11
- package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +32 -0
- /package/dist/web/standalone/.next/static/{ZpFDazP-38PXBPiI_LdZf → NPMNi2HV8BuK3baQsEUhk}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{ZpFDazP-38PXBPiI_LdZf → NPMNi2HV8BuK3baQsEUhk}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// gsd-pi — DB writability probe regression tests (#1234).
|
|
2
|
+
//
|
|
3
|
+
// Auto-start's DB gate only checked that the provider opened, not that the
|
|
4
|
+
// handle can actually write. A schema-current DB does zero writes during open,
|
|
5
|
+
// so a read-only / DBMOVED handle passed the open-only check and failed much
|
|
6
|
+
// later at the first authoritative write with an opaque "readonly database"
|
|
7
|
+
// error. `probeDbWritable` forces a real page write so that failure class is
|
|
8
|
+
// caught at the natural checkpoint.
|
|
9
|
+
|
|
10
|
+
import { test } from "node:test";
|
|
11
|
+
import assert from "node:assert/strict";
|
|
12
|
+
import { mkdtempSync, mkdirSync, rmSync } from "node:fs";
|
|
13
|
+
import { join } from "node:path";
|
|
14
|
+
import { tmpdir } from "node:os";
|
|
15
|
+
|
|
16
|
+
import { openDatabase, closeDatabase, probeDbWritable, _getAdapter } from "../gsd-db.ts";
|
|
17
|
+
import type { DbAdapter } from "../db-adapter.ts";
|
|
18
|
+
|
|
19
|
+
function makeBase(): string {
|
|
20
|
+
const dir = mkdtempSync(join(tmpdir(), "gsd-db-writable-probe-"));
|
|
21
|
+
mkdirSync(join(dir, ".gsd"), { recursive: true });
|
|
22
|
+
return dir;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
test("probeDbWritable succeeds against a freshly opened writable database", () => {
|
|
26
|
+
const base = makeBase();
|
|
27
|
+
assert.equal(openDatabase(join(base, ".gsd", "gsd.db")), true);
|
|
28
|
+
try {
|
|
29
|
+
assert.deepEqual(probeDbWritable(), { ok: true });
|
|
30
|
+
} finally {
|
|
31
|
+
closeDatabase();
|
|
32
|
+
rmSync(base, { recursive: true, force: true });
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test("probeDbWritable reports not-ok with detail when no database is open", () => {
|
|
37
|
+
closeDatabase();
|
|
38
|
+
const result = probeDbWritable();
|
|
39
|
+
assert.equal(result.ok, false);
|
|
40
|
+
assert.match(result.detail ?? "", /No database is open/u);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test("probeDbWritable reports not-ok when the page write fails (simulating a read-only handle)", () => {
|
|
44
|
+
const base = makeBase();
|
|
45
|
+
assert.equal(openDatabase(join(base, ".gsd", "gsd.db")), true);
|
|
46
|
+
const adapter = _getAdapter() as DbAdapter;
|
|
47
|
+
assert.ok(adapter);
|
|
48
|
+
|
|
49
|
+
// The read (prepare/get) still works; only the page-dirtying write throws,
|
|
50
|
+
// mirroring SQLITE_READONLY_DBMOVED which fails once a page is dirtied.
|
|
51
|
+
const origExec = adapter.exec.bind(adapter);
|
|
52
|
+
adapter.exec = (sql: string): void => {
|
|
53
|
+
if (sql.includes("user_version =")) throw new Error("attempt to write a readonly database");
|
|
54
|
+
return origExec(sql);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
try {
|
|
58
|
+
const result = probeDbWritable();
|
|
59
|
+
assert.equal(result.ok, false);
|
|
60
|
+
assert.match(result.detail ?? "", /readonly database/u);
|
|
61
|
+
} finally {
|
|
62
|
+
adapter.exec = origExec;
|
|
63
|
+
closeDatabase();
|
|
64
|
+
rmSync(base, { recursive: true, force: true });
|
|
65
|
+
}
|
|
66
|
+
});
|
|
@@ -109,6 +109,33 @@ test("migrateToFlatPhase creates a backup", async () => {
|
|
|
109
109
|
assert.ok(backups.length >= 1, "at least one migrate-* backup dir should exist");
|
|
110
110
|
});
|
|
111
111
|
|
|
112
|
+
test("failed migration rolls back without leaking a .gsd-backups/migrate-* dir", async (t) => {
|
|
113
|
+
const base = makeTmp();
|
|
114
|
+
const milestonesPath = join(base, ".gsd", "milestones");
|
|
115
|
+
const phasesPath = join(base, ".gsd", "phases");
|
|
116
|
+
|
|
117
|
+
// Fail the pre-render clear of phases/ to drive the rollback path, which by
|
|
118
|
+
// this point has already created the .gsd-backups/migrate-<ts>/ backup.
|
|
119
|
+
const restoreFsOps = _setFlatPhaseMigrationFsOpsForTest({
|
|
120
|
+
rmSync(target, opts) {
|
|
121
|
+
if (target === phasesPath) {
|
|
122
|
+
throw Object.assign(new Error("simulated locked phases/"), { code: "EPERM" });
|
|
123
|
+
}
|
|
124
|
+
return rmSync(target, opts as never);
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
t.after(restoreFsOps);
|
|
128
|
+
|
|
129
|
+
await assert.rejects(migrateToFlatPhase(base), /simulated locked/);
|
|
130
|
+
|
|
131
|
+
assert.ok(existsSync(milestonesPath), "legacy milestones/ should be restored on rollback");
|
|
132
|
+
const backupRoot = join(base, ".gsd-backups");
|
|
133
|
+
const leaked = existsSync(backupRoot)
|
|
134
|
+
? readdirSync(backupRoot).filter((d) => d.startsWith("migrate-"))
|
|
135
|
+
: [];
|
|
136
|
+
assert.equal(leaked.length, 0, "rollback must delete the migrate-* backup it created");
|
|
137
|
+
});
|
|
138
|
+
|
|
112
139
|
test("migrateToFlatPhase preserves milestone/slice/task counts in DB", async () => {
|
|
113
140
|
const base = makeTmp();
|
|
114
141
|
const msBefore = getAllMilestones().length;
|
|
@@ -7,7 +7,7 @@ import { join } from "node:path";
|
|
|
7
7
|
import { tmpdir } from "node:os";
|
|
8
8
|
import { randomUUID } from "node:crypto";
|
|
9
9
|
|
|
10
|
-
import { renderPlanFromDb, renderRoadmapFromDb, renderAssessmentFromDb } from "../markdown-renderer.ts";
|
|
10
|
+
import { renderPlanFromDb, renderRoadmapFromDb, renderAssessmentFromDb, renderReplanFromDb } from "../markdown-renderer.ts";
|
|
11
11
|
import { clearPathCache } from "../paths.ts";
|
|
12
12
|
import { openDatabase, closeDatabase, insertMilestone, insertSlice, insertTask } from "../gsd-db.ts";
|
|
13
13
|
|
|
@@ -83,6 +83,27 @@ test("renderPlanFromDb does NOT write per-task plan files", async () => {
|
|
|
83
83
|
assert.equal(result.taskPlanPaths.length, 0);
|
|
84
84
|
});
|
|
85
85
|
|
|
86
|
+
|
|
87
|
+
// Replan uses the same slice-level target rules as assessment: flat-phase
|
|
88
|
+
// writes must stay at the phase root even if an old slices/SID/ dir exists.
|
|
89
|
+
test("renderReplanFromDb writes to the phase root even when a stray slices/SID/ dir exists", async () => {
|
|
90
|
+
const base = makeTmp();
|
|
91
|
+
const plan = await renderPlanFromDb(base, "M001", "S01");
|
|
92
|
+
const phaseDir = plan.planPath.slice(0, plan.planPath.lastIndexOf("01-01-PLAN.md"));
|
|
93
|
+
mkdirSync(join(phaseDir, "slices", "S01"), { recursive: true });
|
|
94
|
+
clearPathCache();
|
|
95
|
+
|
|
96
|
+
const result = await renderReplanFromDb(base, "M001", "S01", {
|
|
97
|
+
blockerTaskId: "T01",
|
|
98
|
+
blockerDescription: "Need a better path target.",
|
|
99
|
+
whatChanged: "Centralized slice writer path resolution.",
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
assert.match(result.replanPath, /phases[/\\]01-[^/\\]+[/\\]01-01-REPLAN\.md$/);
|
|
103
|
+
assert.ok(!result.replanPath.includes(join("slices", "S01")), "must not write under slices/S01/");
|
|
104
|
+
assert.ok(existsSync(result.replanPath), "replan file should exist at the phase root");
|
|
105
|
+
});
|
|
106
|
+
|
|
86
107
|
// Regression for #1190: a stray slices/SID/ subdir inside a flat-phase milestone
|
|
87
108
|
// must not divert the ASSESSMENT write into slices/SID/ (a hybrid path that
|
|
88
109
|
// auto-mode verification cannot find). It must land at the phase root.
|
|
@@ -9,7 +9,7 @@ import { join } from "node:path";
|
|
|
9
9
|
import assert from "node:assert/strict";
|
|
10
10
|
import test from "node:test";
|
|
11
11
|
|
|
12
|
-
import { relMilestoneFile, resolveMilestoneFile } from "../paths.ts";
|
|
12
|
+
import { normalizeRealPath, relMilestoneFile, relSliceFile, resolveMilestoneFile, targetMilestoneFile } from "../paths.ts";
|
|
13
13
|
|
|
14
14
|
function makeFlatPhaseFixture(): { basePath: string; cleanup: () => void } {
|
|
15
15
|
const basePath = mkdtempSync(join(tmpdir(), "flat-phase-validation-"));
|
|
@@ -33,42 +33,82 @@ function makeFlatPhaseFixture(): { basePath: string; cleanup: () => void } {
|
|
|
33
33
|
return { basePath, cleanup: () => rmSync(basePath, { recursive: true, force: true }) };
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
test("flat-phase: relMilestoneFile resolves to the layout-aware path", () => {
|
|
36
|
+
test("flat-phase: relMilestoneFile resolves to the layout-aware path", (t) => {
|
|
37
37
|
const { basePath, cleanup } = makeFlatPhaseFixture();
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
38
|
+
t.after(cleanup);
|
|
39
|
+
|
|
40
|
+
const rel = relMilestoneFile(basePath, "M001", "VALIDATION");
|
|
41
|
+
assert.equal(rel, ".gsd/phases/01-foo/01-VALIDATION.md");
|
|
42
|
+
const abs = join(basePath, rel);
|
|
43
|
+
assert.equal(existsSync(abs), true);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test("flat-phase: relMilestoneFile fallback uses the milestone title when no directory exists", (t) => {
|
|
47
|
+
const basePath = mkdtempSync(join(tmpdir(), "flat-phase-missing-dir-"));
|
|
48
|
+
t.after(() => rmSync(basePath, { recursive: true, force: true }));
|
|
49
|
+
|
|
50
|
+
const rel = relMilestoneFile(basePath, "M001", "ROADMAP", "Milestone");
|
|
51
|
+
assert.equal(rel, ".gsd/phases/01-milestone/01-ROADMAP.md");
|
|
46
52
|
});
|
|
47
53
|
|
|
48
|
-
test("
|
|
54
|
+
test("legacy layout: relMilestoneFile fallback stays in milestones when the target dir is missing", (t) => {
|
|
55
|
+
const basePath = mkdtempSync(join(tmpdir(), "legacy-missing-dir-"));
|
|
56
|
+
t.after(() => rmSync(basePath, { recursive: true, force: true }));
|
|
57
|
+
const existingLegacyDir = join(basePath, ".gsd", "milestones", "M002");
|
|
58
|
+
mkdirSync(existingLegacyDir, { recursive: true });
|
|
59
|
+
writeFileSync(join(existingLegacyDir, "M002-ROADMAP.md"), "# existing legacy roadmap\n");
|
|
60
|
+
|
|
61
|
+
const rel = relMilestoneFile(basePath, "M001", "ROADMAP", "Milestone");
|
|
62
|
+
|
|
63
|
+
assert.equal(rel, ".gsd/milestones/M001/M001-ROADMAP.md");
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("legacy layout: relSliceFile fallback uses slices directory when target milestone is missing", (t) => {
|
|
67
|
+
const basePath = mkdtempSync(join(tmpdir(), "legacy-missing-slice-"));
|
|
68
|
+
t.after(() => rmSync(basePath, { recursive: true, force: true }));
|
|
69
|
+
const existingLegacyDir = join(basePath, ".gsd", "milestones", "M002");
|
|
70
|
+
mkdirSync(existingLegacyDir, { recursive: true });
|
|
71
|
+
writeFileSync(join(existingLegacyDir, "M002-ROADMAP.md"), "# existing legacy roadmap\n");
|
|
72
|
+
|
|
73
|
+
const rel = relSliceFile(basePath, "M001", "S01", "PLAN", "Milestone");
|
|
74
|
+
|
|
75
|
+
assert.equal(rel, ".gsd/milestones/M001/slices/S01/S01-PLAN.md");
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("flat-phase: targetMilestoneFile ignores legacy-named compatibility files when writing", (t) => {
|
|
79
|
+
const basePath = mkdtempSync(join(tmpdir(), "flat-phase-legacy-named-file-"));
|
|
80
|
+
t.after(() => rmSync(basePath, { recursive: true, force: true }));
|
|
81
|
+
const phaseDir = join(basePath, ".gsd", "phases", "01-foo");
|
|
82
|
+
mkdirSync(phaseDir, { recursive: true });
|
|
83
|
+
writeFileSync(join(phaseDir, "M001-ROADMAP.md"), "# legacy named roadmap\n");
|
|
84
|
+
|
|
85
|
+
assert.equal(
|
|
86
|
+
targetMilestoneFile(basePath, "M001", "ROADMAP", "Foo"),
|
|
87
|
+
join(normalizeRealPath(phaseDir), "01-ROADMAP.md"),
|
|
88
|
+
);
|
|
89
|
+
assert.equal(relMilestoneFile(basePath, "M001", "ROADMAP", "Foo"), ".gsd/phases/01-foo/M001-ROADMAP.md");
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
test("flat-phase: resolveMilestoneFile finds the on-disk file", (t) => {
|
|
49
93
|
const { basePath, cleanup } = makeFlatPhaseFixture();
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
cleanup();
|
|
57
|
-
}
|
|
94
|
+
t.after(cleanup);
|
|
95
|
+
|
|
96
|
+
const abs = resolveMilestoneFile(basePath, "M001", "VALIDATION");
|
|
97
|
+
assert.ok(abs, "resolveMilestoneFile must return a path for the flat-phase fixture");
|
|
98
|
+
assert.equal(abs!.endsWith("/.gsd/phases/01-foo/01-VALIDATION.md"), true);
|
|
99
|
+
assert.equal(existsSync(abs!), true);
|
|
58
100
|
});
|
|
59
101
|
|
|
60
|
-
test("flat-phase: the legacy hardcoded path does NOT resolve (regression for #876)", () => {
|
|
102
|
+
test("flat-phase: the legacy hardcoded path does NOT resolve (regression for #876)", (t) => {
|
|
61
103
|
const { basePath, cleanup } = makeFlatPhaseFixture();
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
cleanup();
|
|
73
|
-
}
|
|
104
|
+
t.after(cleanup);
|
|
105
|
+
|
|
106
|
+
// This is the path that auto-verification.ts:264-270 used to construct.
|
|
107
|
+
// It must NOT exist on a flat-phase project — that's the whole bug.
|
|
108
|
+
const legacyHardcoded = join(basePath, ".gsd", "milestones", "M001", "M001-VALIDATION.md");
|
|
109
|
+
assert.equal(
|
|
110
|
+
existsSync(legacyHardcoded),
|
|
111
|
+
false,
|
|
112
|
+
"legacy hardcoded path must not exist in flat-phase fixture — if it does, the test fixture is wrong",
|
|
113
|
+
);
|
|
74
114
|
});
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
// Project/App: gsd-pi
|
|
2
|
+
// File Purpose: Regression for #1222 — a re-import must not silently downgrade
|
|
3
|
+
// a completed flat-phase task back to pending when its SUMMARY.md attests the
|
|
4
|
+
// completion. gsd_task_complete succeeds and writes TID-SUMMARY.md, but a stale
|
|
5
|
+
// plan checkbox re-imported into the DB would revert the task to pending,
|
|
6
|
+
// hard-stopping auto-mode on its "state did not advance" guard.
|
|
7
|
+
|
|
8
|
+
import test, { afterEach } from "node:test";
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
import { cpSync, mkdtempSync, rmSync, writeFileSync, unlinkSync, mkdirSync } from "node:fs";
|
|
11
|
+
import { join, dirname } from "node:path";
|
|
12
|
+
import { tmpdir } from "node:os";
|
|
13
|
+
import { randomUUID } from "node:crypto";
|
|
14
|
+
import { fileURLToPath } from "node:url";
|
|
15
|
+
|
|
16
|
+
import { openDatabase, closeDatabase, getAllMilestones, getMilestoneSlices, getSliceTasks, repairTaskCompletionFromSummary } from "../gsd-db.ts";
|
|
17
|
+
import { migrateHierarchyToDb } from "../md-importer.ts";
|
|
18
|
+
import { invalidateStateCache } from "../state.ts";
|
|
19
|
+
|
|
20
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
const FIXTURE_ROOT = join(__dirname, "__fixtures__", "flat-phase");
|
|
22
|
+
// The flat-phase fixture parses to M001/S01/T01 (phase 01, plan 01, task T01).
|
|
23
|
+
const PHASE_DIR = join(".gsd", "phases", "01-foundation");
|
|
24
|
+
const SUMMARY_REL = join(PHASE_DIR, "T01-SUMMARY.md");
|
|
25
|
+
|
|
26
|
+
const tmpDirs: string[] = [];
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
closeDatabase();
|
|
29
|
+
for (const d of tmpDirs) { try { rmSync(d, { recursive: true, force: true }); } catch { /* */ } }
|
|
30
|
+
tmpDirs.length = 0;
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
function copyFixture(): string {
|
|
34
|
+
const base = mkdtempSync(join(tmpdir(), `gsd-lostupdate-${randomUUID()}`));
|
|
35
|
+
cpSync(FIXTURE_ROOT, base, { recursive: true });
|
|
36
|
+
tmpDirs.push(base);
|
|
37
|
+
return base;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function taskStatus(base: string): string | undefined {
|
|
41
|
+
return t01Row()?.status;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function t01Row() {
|
|
45
|
+
const ms = getAllMilestones();
|
|
46
|
+
if (ms.length === 0) return undefined;
|
|
47
|
+
const slices = getMilestoneSlices(ms[0]!.id);
|
|
48
|
+
if (slices.length === 0) return undefined;
|
|
49
|
+
const tasks = getSliceTasks(ms[0]!.id, slices[0]!.id);
|
|
50
|
+
return tasks.find((t) => t.id === "T01");
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function t01Ids() {
|
|
54
|
+
const ms = getAllMilestones();
|
|
55
|
+
const slices = getMilestoneSlices(ms[0]!.id);
|
|
56
|
+
return { milestoneId: ms[0]!.id, sliceId: slices[0]!.id };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
test("re-import keeps a flat-phase task complete when its SUMMARY.md is present (#1222)", () => {
|
|
60
|
+
const base = copyFixture();
|
|
61
|
+
// The fixture plan leaves T01 unchecked ([ ]). Simulate a completed task: the
|
|
62
|
+
// durable SUMMARY.md exists even though the plan checkbox is stale-unchecked.
|
|
63
|
+
writeFileSync(join(base, SUMMARY_REL), "# T01 SUMMARY\n\nDone.\n");
|
|
64
|
+
|
|
65
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
66
|
+
migrateHierarchyToDb(base);
|
|
67
|
+
invalidateStateCache();
|
|
68
|
+
|
|
69
|
+
assert.equal(
|
|
70
|
+
taskStatus(base),
|
|
71
|
+
"complete",
|
|
72
|
+
"task with SUMMARY.md on disk must import as complete, not revert to pending",
|
|
73
|
+
);
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test("re-import leaves an unchecked flat-phase task pending when no SUMMARY.md exists (#1222)", () => {
|
|
77
|
+
const base = copyFixture();
|
|
78
|
+
// No SUMMARY.md — the unchecked checkbox is the authoritative signal.
|
|
79
|
+
|
|
80
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
81
|
+
migrateHierarchyToDb(base);
|
|
82
|
+
invalidateStateCache();
|
|
83
|
+
|
|
84
|
+
assert.equal(
|
|
85
|
+
taskStatus(base),
|
|
86
|
+
"pending",
|
|
87
|
+
"unchecked task without a SUMMARY.md must stay pending",
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test("re-import keeps a flat-phase task complete even when an auxiliary tasks/ subdir exists (#1222)", () => {
|
|
92
|
+
const base = copyFixture();
|
|
93
|
+
// Flat-phase may keep a tasks/ subdir for gate artifacts (e.g. T01-VERIFY.json)
|
|
94
|
+
// while gsd_task_complete still writes TID-SUMMARY.md at the phase root. The
|
|
95
|
+
// tasks/ subdir must NOT shadow that real summary and downgrade the task.
|
|
96
|
+
mkdirSync(join(base, PHASE_DIR, "tasks"), { recursive: true });
|
|
97
|
+
writeFileSync(join(base, PHASE_DIR, "tasks", "T01-VERIFY.json"), "{}\n");
|
|
98
|
+
writeFileSync(join(base, SUMMARY_REL), "# T01 SUMMARY\n\nDone.\n");
|
|
99
|
+
|
|
100
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
101
|
+
migrateHierarchyToDb(base);
|
|
102
|
+
invalidateStateCache();
|
|
103
|
+
|
|
104
|
+
assert.equal(
|
|
105
|
+
taskStatus(base),
|
|
106
|
+
"complete",
|
|
107
|
+
"a phase-root SUMMARY.md must win even when a tasks/ subdir exists (no shadowing)",
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("re-import preserves execution metadata and completed_at of an attested-complete task (#1222)", () => {
|
|
112
|
+
const base = copyFixture();
|
|
113
|
+
// T01 is completed on disk (SUMMARY present) but its plan checkbox is stale-unchecked.
|
|
114
|
+
writeFileSync(join(base, SUMMARY_REL), "# T01 SUMMARY\n\nDone.\n");
|
|
115
|
+
|
|
116
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
117
|
+
migrateHierarchyToDb(base);
|
|
118
|
+
invalidateStateCache();
|
|
119
|
+
|
|
120
|
+
// Simulate the row gsd_task_complete leaves behind: real execution prose plus
|
|
121
|
+
// a completion timestamp from when the task actually finished.
|
|
122
|
+
const OLD_COMPLETED_AT = "2026-07-01T00:00:00.000Z";
|
|
123
|
+
const REAL_ONE_LINER = "Implemented the widget frobnicator end-to-end";
|
|
124
|
+
const REAL_SUMMARY = "# T01 full summary\n\nEverything about how T01 was done.";
|
|
125
|
+
const REAL_VERIFICATION = "all 12 checks passed";
|
|
126
|
+
const { milestoneId, sliceId } = t01Ids();
|
|
127
|
+
repairTaskCompletionFromSummary({
|
|
128
|
+
milestoneId,
|
|
129
|
+
sliceId,
|
|
130
|
+
taskId: "T01",
|
|
131
|
+
oneLiner: REAL_ONE_LINER,
|
|
132
|
+
verificationResult: REAL_VERIFICATION,
|
|
133
|
+
completedAt: OLD_COMPLETED_AT,
|
|
134
|
+
fullSummaryMd: REAL_SUMMARY,
|
|
135
|
+
});
|
|
136
|
+
invalidateStateCache();
|
|
137
|
+
|
|
138
|
+
const before = t01Row()!;
|
|
139
|
+
assert.equal(before.status, "complete", "precondition: T01 is complete");
|
|
140
|
+
assert.equal(before.one_liner, REAL_ONE_LINER, "precondition: one_liner set");
|
|
141
|
+
assert.equal(before.completed_at, OLD_COMPLETED_AT, "precondition: completed_at set");
|
|
142
|
+
|
|
143
|
+
// The pre-dispatch reconcile re-imports the whole tree (plan checkbox still [ ]).
|
|
144
|
+
migrateHierarchyToDb(base);
|
|
145
|
+
invalidateStateCache();
|
|
146
|
+
|
|
147
|
+
const after = t01Row()!;
|
|
148
|
+
assert.equal(after.status, "complete", "status must survive re-import");
|
|
149
|
+
assert.equal(after.one_liner, REAL_ONE_LINER, "one_liner must survive re-import");
|
|
150
|
+
assert.equal(after.full_summary_md, REAL_SUMMARY, "full_summary_md must survive re-import");
|
|
151
|
+
assert.equal(after.verification_result, REAL_VERIFICATION, "verification_result must survive re-import");
|
|
152
|
+
assert.equal(after.completed_at, OLD_COMPLETED_AT, "completed_at must not be refreshed by re-import");
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("removing the SUMMARY.md (reopen) lets a re-import return the task to pending and clears completion metadata (#1222)", () => {
|
|
156
|
+
const base = copyFixture();
|
|
157
|
+
writeFileSync(join(base, SUMMARY_REL), "# T01 SUMMARY\n\nDone.\n");
|
|
158
|
+
|
|
159
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
160
|
+
migrateHierarchyToDb(base);
|
|
161
|
+
invalidateStateCache();
|
|
162
|
+
assert.equal(taskStatus(base), "complete", "precondition: task imports complete with SUMMARY present");
|
|
163
|
+
|
|
164
|
+
// Simulate the durable completion row gsd_task_complete leaves behind.
|
|
165
|
+
const { milestoneId, sliceId } = t01Ids();
|
|
166
|
+
repairTaskCompletionFromSummary({
|
|
167
|
+
milestoneId,
|
|
168
|
+
sliceId,
|
|
169
|
+
taskId: "T01",
|
|
170
|
+
oneLiner: "Implemented the widget frobnicator end-to-end",
|
|
171
|
+
verificationResult: "all 12 checks passed",
|
|
172
|
+
completedAt: "2026-07-01T00:00:00.000Z",
|
|
173
|
+
fullSummaryMd: "# T01 full summary\n\nEverything about how T01 was done.",
|
|
174
|
+
});
|
|
175
|
+
invalidateStateCache();
|
|
176
|
+
const before = t01Row()!;
|
|
177
|
+
assert.equal(before.status, "complete", "precondition: T01 is complete");
|
|
178
|
+
assert.equal(before.completed_at, "2026-07-01T00:00:00.000Z", "precondition: completed_at set");
|
|
179
|
+
|
|
180
|
+
// reopen-task deletes the flat-phase SUMMARY; the next re-import must respect it.
|
|
181
|
+
unlinkSync(join(base, SUMMARY_REL));
|
|
182
|
+
migrateHierarchyToDb(base);
|
|
183
|
+
invalidateStateCache();
|
|
184
|
+
const after = t01Row()!;
|
|
185
|
+
assert.equal(
|
|
186
|
+
after.status,
|
|
187
|
+
"pending",
|
|
188
|
+
"after the SUMMARY is removed, a re-import must return the unchecked task to pending",
|
|
189
|
+
);
|
|
190
|
+
// A pending re-import must not preserve stale completion metadata (#1228 thread):
|
|
191
|
+
// preserveCompletionMetadata only applies to complete/done imports.
|
|
192
|
+
assert.ok(!after.completed_at, "completed_at must be cleared when the task reverts to pending");
|
|
193
|
+
assert.ok(!after.one_liner, "one_liner must be cleared when the task reverts to pending");
|
|
194
|
+
assert.ok(!after.full_summary_md, "full_summary_md must be cleared when the task reverts to pending");
|
|
195
|
+
assert.ok(!after.verification_result, "verification_result must be cleared when the task reverts to pending");
|
|
196
|
+
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { describe, test } from 'node:test';
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
|
-
import { mkdtempSync, mkdirSync, rmSync, realpathSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { mkdtempSync, mkdirSync, rmSync, realpathSync, symlinkSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
import { spawnSync } from "node:child_process";
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
gsdProjectionRoot,
|
|
10
10
|
gsdRoot,
|
|
11
11
|
milestonesDir,
|
|
12
|
+
resolveGsdPathContract,
|
|
12
13
|
resolveSliceFile,
|
|
13
14
|
resolveTaskFile,
|
|
14
15
|
_clearGsdRootCache,
|
|
@@ -166,4 +167,90 @@ describe('paths', () => {
|
|
|
166
167
|
cleanup(root);
|
|
167
168
|
}
|
|
168
169
|
});
|
|
170
|
+
|
|
171
|
+
test('Case 9: flat-phase task SUMMARY resolves at phase root despite a stray slices/SID/ dir', () => {
|
|
172
|
+
const root = tmp();
|
|
173
|
+
try {
|
|
174
|
+
// Hybrid flat-phase milestone: phase dir holds flat TID-SUMMARY.md, but a
|
|
175
|
+
// stray slices/S01/ folder also exists on disk. resolveSlicePath then points
|
|
176
|
+
// under slices/, so layout (phases/ vs milestones/) — not slicePath===phaseDir
|
|
177
|
+
// — must decide the flat-phase task summary location.
|
|
178
|
+
const phaseDir = join(root, ".gsd", "phases", "01-foo");
|
|
179
|
+
mkdirSync(join(phaseDir, "slices", "S01"), { recursive: true });
|
|
180
|
+
const flatSummary = join(phaseDir, "T01-SUMMARY.md");
|
|
181
|
+
writeFileSync(flatSummary, "# task summary\n");
|
|
182
|
+
|
|
183
|
+
_clearGsdRootCache();
|
|
184
|
+
|
|
185
|
+
assert.deepStrictEqual(
|
|
186
|
+
resolveTaskFile(root, "M001", "S01", "T01", "SUMMARY"),
|
|
187
|
+
flatSummary,
|
|
188
|
+
"flat-phase TID-SUMMARY.md at phase root is found even when slices/S01/ exists",
|
|
189
|
+
);
|
|
190
|
+
} finally { cleanup(root); }
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test('Case 10: resolveGsdPathContract canonicalizes a symlinked .gsd root (regression #1239)', () => {
|
|
194
|
+
// WSL / external-state shape: `<projectRoot>/.gsd` is a symlink pointing at a
|
|
195
|
+
// real directory on another volume. Before #1239 the DB path was built from
|
|
196
|
+
// the unresolved symlink, so the workflow SQLite handle opened through a
|
|
197
|
+
// move-prone path and could pick the wrong journal mode / SQLITE_READONLY_DBMOVED.
|
|
198
|
+
const root = tmp();
|
|
199
|
+
try {
|
|
200
|
+
const realState = join(root, "real-state"); // canonical symlink target
|
|
201
|
+
mkdirSync(join(realState, "milestones"), { recursive: true });
|
|
202
|
+
const projectRoot = join(root, "project");
|
|
203
|
+
mkdirSync(projectRoot, { recursive: true });
|
|
204
|
+
symlinkSync(realState, join(projectRoot, ".gsd"), "dir");
|
|
205
|
+
|
|
206
|
+
_clearGsdRootCache();
|
|
207
|
+
const contract = resolveGsdPathContract(projectRoot);
|
|
208
|
+
|
|
209
|
+
const canonicalGsd = realpathSync.native(join(projectRoot, ".gsd"));
|
|
210
|
+
assert.deepStrictEqual(contract.projectGsd, canonicalGsd,
|
|
211
|
+
"projectGsd is the realpath'd target, not the unresolved .gsd symlink");
|
|
212
|
+
assert.deepStrictEqual(contract.projectDb, join(canonicalGsd, "gsd.db"),
|
|
213
|
+
"projectDb is built from the canonical .gsd root");
|
|
214
|
+
assert.deepStrictEqual(contract.projectGsd, gsdProjectionRoot(projectRoot),
|
|
215
|
+
"projectGsd agrees with gsdProjectionRoot on the canonical root");
|
|
216
|
+
assert.notStrictEqual(contract.projectDb, join(projectRoot, ".gsd", "gsd.db"),
|
|
217
|
+
"regression #1239: DB must not open through the unresolved .gsd symlink");
|
|
218
|
+
} finally { cleanup(root); }
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
test('Case 11: resolveGsdPathContract canonicalizes the external-state worktree DB path through a symlinked store (regression #1239)', () => {
|
|
222
|
+
const root = tmp();
|
|
223
|
+
const originalStateDir = process.env.GSD_STATE_DIR;
|
|
224
|
+
try {
|
|
225
|
+
const stateDir = join(root, "state");
|
|
226
|
+
process.env.GSD_STATE_DIR = stateDir;
|
|
227
|
+
|
|
228
|
+
// The external-state project store `<state>/projects/<hash>` is itself a
|
|
229
|
+
// symlink to a real directory on another volume (the WSL `.gsd` shape).
|
|
230
|
+
const realProjectGsd = join(root, "real-projects", "abc123");
|
|
231
|
+
mkdirSync(realProjectGsd, { recursive: true });
|
|
232
|
+
mkdirSync(join(stateDir, "projects"), { recursive: true });
|
|
233
|
+
const storeSymlink = join(stateDir, "projects", "abc123");
|
|
234
|
+
symlinkSync(realProjectGsd, storeSymlink, "dir");
|
|
235
|
+
|
|
236
|
+
const wtRoot = join(storeSymlink, "worktrees", "M002");
|
|
237
|
+
mkdirSync(join(wtRoot, ".gsd"), { recursive: true });
|
|
238
|
+
|
|
239
|
+
_clearGsdRootCache();
|
|
240
|
+
const contract = resolveGsdPathContract(wtRoot);
|
|
241
|
+
|
|
242
|
+
const canonicalStore = realpathSync.native(storeSymlink);
|
|
243
|
+
assert.ok(contract.isWorktree, "recognized as an external-state worktree layout");
|
|
244
|
+
assert.deepStrictEqual(contract.projectGsd, canonicalStore,
|
|
245
|
+
"projectGsd is the realpath'd project store, not the unresolved symlink");
|
|
246
|
+
assert.deepStrictEqual(contract.projectDb, join(canonicalStore, "gsd.db"),
|
|
247
|
+
"projectDb is built from the canonical project store");
|
|
248
|
+
assert.notStrictEqual(contract.projectDb, join(storeSymlink, "gsd.db"),
|
|
249
|
+
"regression #1239: DB must not open through the unresolved external-state symlink");
|
|
250
|
+
} finally {
|
|
251
|
+
if (originalStateDir === undefined) delete process.env.GSD_STATE_DIR;
|
|
252
|
+
else process.env.GSD_STATE_DIR = originalStateDir;
|
|
253
|
+
cleanup(root);
|
|
254
|
+
}
|
|
255
|
+
});
|
|
169
256
|
});
|
|
@@ -40,6 +40,7 @@ import { readUnitRuntimeRecord } from "../unit-runtime.js";
|
|
|
40
40
|
import { ModelPolicyDispatchBlockedError } from "../auto-model-selection.js";
|
|
41
41
|
import {
|
|
42
42
|
closeDatabase,
|
|
43
|
+
getTask,
|
|
43
44
|
insertMilestone,
|
|
44
45
|
insertSlice,
|
|
45
46
|
insertTask,
|
|
@@ -445,7 +446,7 @@ test("runDispatch retries when complete-milestone summary exists on disk and stu
|
|
|
445
446
|
assert.equal(stopCalls, 0, "artifact-based recovery should not hard-stop the loop");
|
|
446
447
|
});
|
|
447
448
|
|
|
448
|
-
test("runDispatch
|
|
449
|
+
test("runDispatch promotes execute-task DB status when artifacts exist but DB status is still open (#1204)", async (t) => {
|
|
449
450
|
const capture = createEventCapture();
|
|
450
451
|
let pauseCalls = 0;
|
|
451
452
|
let stopCalls = 0;
|
|
@@ -522,16 +523,16 @@ test("runDispatch pauses when execute-task artifacts exist but DB status is stil
|
|
|
522
523
|
|
|
523
524
|
const result = await runDispatch(ic, preData, loopState);
|
|
524
525
|
|
|
525
|
-
assert.equal(result.action, "
|
|
526
|
-
assert.equal(
|
|
527
|
-
assert.equal(
|
|
528
|
-
assert.equal(
|
|
529
|
-
assert.equal(
|
|
530
|
-
assert.equal(loopState.
|
|
531
|
-
assert.equal(
|
|
526
|
+
assert.equal(result.action, "continue");
|
|
527
|
+
assert.equal(pauseCalls, 0, "disk-verified promotion should not pause auto-mode");
|
|
528
|
+
assert.equal(stopCalls, 0, "disk-verified promotion should not hard-stop the loop");
|
|
529
|
+
assert.equal(invalidateCalls, 1, "successful promotion should invalidate caches before retrying");
|
|
530
|
+
assert.equal(loopState.recentUnits.length, 0, "successful promotion should clear the stuck window");
|
|
531
|
+
assert.equal(loopState.stuckRecoveryAttempts, 1, "Level 1 recovery increments the attempt counter");
|
|
532
|
+
assert.equal(getTask("M001", "S01", "T01")?.status, "complete", "the open DB task should be promoted to complete");
|
|
532
533
|
});
|
|
533
534
|
|
|
534
|
-
test("runDispatch
|
|
535
|
+
test("runDispatch promotes execute-task DB status at Level 2 when artifacts exist but DB status is still open (#1204)", async (t) => {
|
|
535
536
|
const capture = createEventCapture();
|
|
536
537
|
let pauseCalls = 0;
|
|
537
538
|
let stopCalls = 0;
|
|
@@ -601,13 +602,13 @@ test("runDispatch pauses at Level 2 when execute-task artifacts exist but DB sta
|
|
|
601
602
|
|
|
602
603
|
const result = await runDispatch(ic, preData, loopState);
|
|
603
604
|
|
|
604
|
-
assert.equal(result.action, "
|
|
605
|
-
assert.equal(
|
|
606
|
-
assert.equal(
|
|
607
|
-
assert.equal(stopCalls, 0, "Level 2 execute-task disk/db mismatch should not hard-stop the loop");
|
|
605
|
+
assert.equal(result.action, "continue");
|
|
606
|
+
assert.equal(pauseCalls, 0, "Level 2 disk-verified promotion should not pause auto-mode");
|
|
607
|
+
assert.equal(stopCalls, 0, "Level 2 disk-verified promotion should not hard-stop the loop");
|
|
608
608
|
assert.equal(invalidateCalls, 1, "Level 2 should invalidate caches before the final artifact recheck");
|
|
609
|
-
assert.equal(loopState.recentUnits.length,
|
|
610
|
-
assert.equal(loopState.stuckRecoveryAttempts, 1, "Level 2
|
|
609
|
+
assert.equal(loopState.recentUnits.length, 0, "Level 2 successful promotion should clear the stuck window");
|
|
610
|
+
assert.equal(loopState.stuckRecoveryAttempts, 1, "Level 2 does not reset the recovery counter");
|
|
611
|
+
assert.equal(getTask("M001", "S01", "T01")?.status, "complete", "the open DB task should be promoted to complete");
|
|
611
612
|
});
|
|
612
613
|
|
|
613
614
|
test("runDispatch clears execute-task stuck state when artifacts and DB status are complete", async (t) => {
|