@opengsd/gsd-pi 1.4.0-dev.c18009cd → 1.5.0-dev.4fb2d460
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 +10 -10
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/ask-user-questions.js +1 -1
- package/dist/resources/extensions/gsd/auto/closeout.js +97 -5
- package/dist/resources/extensions/gsd/auto/orchestrator.js +4 -3
- package/dist/resources/extensions/gsd/auto/pre-dispatch.js +6 -3
- package/dist/resources/extensions/gsd/auto/unit-phase.js +5 -9
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +12 -3
- package/dist/resources/extensions/gsd/auto-direct-dispatch.js +11 -7
- package/dist/resources/extensions/gsd/auto-dispatch.js +16 -2
- package/dist/resources/extensions/gsd/auto-recovery.js +50 -6
- package/dist/resources/extensions/gsd/auto-start.js +4 -3
- package/dist/resources/extensions/gsd/auto-worktree-branch-lifecycle.js +189 -0
- package/dist/resources/extensions/gsd/auto-worktree-cleanup.js +133 -0
- package/dist/resources/extensions/gsd/auto-worktree-conflict-auto-resolve.js +35 -0
- package/dist/resources/extensions/gsd/auto-worktree-creation.js +130 -0
- package/dist/resources/extensions/gsd/auto-worktree-entry.js +118 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-already-merged.js +98 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-branch-head.js +52 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-cleanup.js +87 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-code-changes.js +46 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-db-ready.js +68 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-integration-branch.js +51 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-message.js +106 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-pre-teardown.js +64 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge-stash.js +168 -0
- package/dist/resources/extensions/gsd/auto-worktree-merge.js +302 -0
- package/dist/resources/extensions/gsd/auto-worktree-milestone-shelter.js +119 -0
- package/dist/resources/extensions/gsd/auto-worktree-path-resolution.js +5 -0
- package/dist/resources/extensions/gsd/auto-worktree-path-utils.js +5 -0
- package/dist/resources/extensions/gsd/auto-worktree-resource-version.js +41 -0
- package/dist/resources/extensions/gsd/auto-worktree-runtime-cleanup.js +99 -0
- package/dist/resources/extensions/gsd/auto-worktree-session-registry.js +57 -0
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +249 -0
- package/dist/resources/extensions/gsd/auto-worktree-teardown.js +120 -0
- package/dist/resources/extensions/gsd/auto-worktree.js +20 -2073
- package/dist/resources/extensions/gsd/auto.js +9 -3
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +1 -1
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +20 -2
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +1 -1
- package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +185 -27
- package/dist/resources/extensions/gsd/codebase-generator.js +7 -1
- package/dist/resources/extensions/gsd/commands-handlers.js +1 -1
- package/dist/resources/extensions/gsd/commands-workflow-templates.js +3 -3
- package/dist/resources/extensions/gsd/db/queries.js +9 -9
- package/dist/resources/extensions/gsd/db/writers/cascades.js +20 -2
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +93 -11
- package/dist/resources/extensions/gsd/flat-phase-migration.js +32 -3
- package/dist/resources/extensions/gsd/forensics.js +1 -1
- package/dist/resources/extensions/gsd/git-conflict-resolve.js +2 -2
- package/dist/resources/extensions/gsd/git-service.js +45 -0
- package/dist/resources/extensions/gsd/gsd-db.js +23 -11
- package/dist/resources/extensions/gsd/guided-flow.js +22 -17
- package/dist/resources/extensions/gsd/markdown-renderer.js +15 -3
- package/dist/resources/extensions/gsd/md-importer.js +89 -23
- package/dist/resources/extensions/gsd/migration-auto-check.js +30 -3
- package/dist/resources/extensions/gsd/milestone-merge-transaction.js +11 -0
- package/dist/resources/extensions/gsd/milestone-settlement.js +1 -1
- package/dist/resources/extensions/gsd/parallel-merge.js +1 -1
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +3 -1
- package/dist/resources/extensions/gsd/paths.js +17 -3
- package/dist/resources/extensions/gsd/preferences-skills.js +10 -16
- package/dist/resources/extensions/gsd/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +82 -0
- package/dist/resources/extensions/gsd/preferences.js +20 -0
- package/dist/resources/extensions/gsd/prompts/system.md +6 -6
- package/dist/resources/extensions/gsd/quick.js +12 -3
- package/dist/resources/extensions/gsd/skill-discovery.js +36 -23
- package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +2 -1
- package/dist/resources/extensions/gsd/state/derive/from-db.js +88 -153
- package/dist/resources/extensions/gsd/tool-contract.js +14 -0
- 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/unit-registry.js +12 -2
- package/dist/resources/extensions/gsd/unmerged-milestone-guard.js +1 -1
- package/dist/resources/extensions/gsd/validation-block-guard.js +2 -1
- package/dist/resources/extensions/gsd/workspace-git-preflight.js +2 -2
- package/dist/resources/extensions/gsd/worktree-command.js +1 -1
- package/dist/resources/extensions/gsd/worktree-lifecycle.js +14 -9
- package/dist/resources/extensions/gsd/worktree-reentry.js +2 -1
- 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 +8 -8
- 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/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 +8 -8
- 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/web/standalone/node_modules/@gsd/native/package.json +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
- package/integrations/hermes/open_gsd_hermes/gsd_client.py +24 -35
- package/integrations/hermes/tests/test_gsd_client_timeout.py +4 -4
- package/package.json +6 -6
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/dist/export-html/template.js +53 -4
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +3 -2
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- 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/dist/agent-loop.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +14 -9
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/index.d.ts +1 -0
- package/packages/pi-ai/dist/index.d.ts.map +1 -1
- package/packages/pi-ai/dist/index.js +1 -0
- package/packages/pi-ai/dist/index.js.map +1 -1
- package/packages/pi-ai/dist/providers/transform-messages.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/transform-messages.js +7 -3
- package/packages/pi-ai/dist/providers/transform-messages.js.map +1 -1
- package/packages/pi-ai/dist/tool-result-content.d.ts +3 -0
- package/packages/pi-ai/dist/tool-result-content.d.ts.map +1 -0
- package/packages/pi-ai/dist/tool-result-content.js +62 -0
- package/packages/pi-ai/dist/tool-result-content.js.map +1 -0
- package/packages/pi-ai/dist/types.d.ts +8 -20
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +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/dist/core/export-html/template.js +53 -4
- package/pkg/package.json +1 -1
- package/src/resources/extensions/ask-user-questions.ts +1 -1
- package/src/resources/extensions/gsd/auto/closeout.ts +114 -7
- package/src/resources/extensions/gsd/auto/orchestrator.ts +4 -3
- package/src/resources/extensions/gsd/auto/pre-dispatch.ts +6 -3
- package/src/resources/extensions/gsd/auto/unit-phase.ts +5 -9
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +12 -3
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +13 -7
- package/src/resources/extensions/gsd/auto-dispatch.ts +18 -2
- package/src/resources/extensions/gsd/auto-recovery.ts +50 -6
- package/src/resources/extensions/gsd/auto-start.ts +5 -3
- package/src/resources/extensions/gsd/auto-worktree-branch-lifecycle.ts +237 -0
- package/src/resources/extensions/gsd/auto-worktree-cleanup.ts +166 -0
- package/src/resources/extensions/gsd/auto-worktree-conflict-auto-resolve.ts +37 -0
- package/src/resources/extensions/gsd/auto-worktree-creation.ts +171 -0
- package/src/resources/extensions/gsd/auto-worktree-entry.ts +154 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-already-merged.ts +161 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-branch-head.ts +83 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-cleanup.ts +132 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-code-changes.ts +71 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-db-ready.ts +112 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-integration-branch.ts +99 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-message.ts +166 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-pre-teardown.ts +93 -0
- package/src/resources/extensions/gsd/auto-worktree-merge-stash.ts +213 -0
- package/src/resources/extensions/gsd/auto-worktree-merge.ts +391 -0
- package/src/resources/extensions/gsd/auto-worktree-milestone-shelter.ts +159 -0
- package/src/resources/extensions/gsd/auto-worktree-path-resolution.ts +6 -0
- package/src/resources/extensions/gsd/auto-worktree-path-utils.ts +10 -0
- package/src/resources/extensions/gsd/auto-worktree-resource-version.ts +44 -0
- package/src/resources/extensions/gsd/auto-worktree-runtime-cleanup.ts +119 -0
- package/src/resources/extensions/gsd/auto-worktree-session-registry.ts +71 -0
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +370 -0
- package/src/resources/extensions/gsd/auto-worktree-teardown.ts +161 -0
- package/src/resources/extensions/gsd/auto-worktree.ts +69 -2429
- package/src/resources/extensions/gsd/auto.ts +11 -17
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +1 -1
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +20 -2
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +1 -1
- package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +218 -27
- package/src/resources/extensions/gsd/codebase-generator.ts +7 -1
- package/src/resources/extensions/gsd/commands-handlers.ts +1 -1
- package/src/resources/extensions/gsd/commands-workflow-templates.ts +3 -3
- package/src/resources/extensions/gsd/db/queries.ts +9 -9
- package/src/resources/extensions/gsd/db/writers/cascades.ts +23 -1
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +109 -10
- package/src/resources/extensions/gsd/flat-phase-migration.ts +34 -3
- package/src/resources/extensions/gsd/forensics.ts +1 -1
- package/src/resources/extensions/gsd/git-conflict-resolve.ts +2 -2
- package/src/resources/extensions/gsd/git-service.ts +53 -0
- package/src/resources/extensions/gsd/gsd-db.ts +33 -11
- package/src/resources/extensions/gsd/guided-flow.ts +26 -18
- package/src/resources/extensions/gsd/markdown-renderer.ts +15 -3
- package/src/resources/extensions/gsd/md-importer.ts +93 -22
- package/src/resources/extensions/gsd/migration-auto-check.ts +32 -3
- package/src/resources/extensions/gsd/milestone-merge-transaction.ts +15 -0
- package/src/resources/extensions/gsd/milestone-settlement.ts +1 -1
- package/src/resources/extensions/gsd/parallel-merge.ts +1 -1
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +3 -1
- package/src/resources/extensions/gsd/paths.ts +21 -3
- package/src/resources/extensions/gsd/preferences-skills.ts +10 -16
- package/src/resources/extensions/gsd/preferences-types.ts +30 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +69 -0
- package/src/resources/extensions/gsd/preferences.ts +20 -0
- package/src/resources/extensions/gsd/prompts/system.md +6 -6
- package/src/resources/extensions/gsd/quick.ts +11 -3
- package/src/resources/extensions/gsd/skill-discovery.ts +47 -23
- package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +3 -1
- package/src/resources/extensions/gsd/state/derive/from-db.ts +201 -157
- package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +169 -1
- package/src/resources/extensions/gsd/tests/auto-loop-no-copy-artifacts.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +57 -0
- package/src/resources/extensions/gsd/tests/auto-start-clean-runtime-db-gated.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-warning-noise-regression.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/auto-worktree-auto-resolve.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-already-merged.test.ts +177 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-branch-head.test.ts +118 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-cleanup.test.ts +115 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-code-changes.test.ts +76 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-db-ready.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-integration-branch.test.ts +123 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-message.test.ts +114 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-pre-teardown.test.ts +84 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-merge-stash.test.ts +70 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-shelter.test.ts +52 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +6 -6
- package/src/resources/extensions/gsd/tests/auto-worktree-resource-version.test.ts +59 -0
- package/src/resources/extensions/gsd/tests/auto-worktree-untracked-content.test.ts +3 -1
- package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +174 -0
- package/src/resources/extensions/gsd/tests/fast-forward-reused-milestone-branch.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +71 -1
- package/src/resources/extensions/gsd/tests/flat-phase-renderer.test.ts +25 -1
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +40 -0
- package/src/resources/extensions/gsd/tests/import-completion-lost-update.test.ts +196 -0
- package/src/resources/extensions/gsd/tests/integration/all-milestones-complete-merge.test.ts +4 -6
- package/src/resources/extensions/gsd/tests/integration/auto-stash-merge.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +7 -8
- package/src/resources/extensions/gsd/tests/integration/feature-branch-lifecycle-integration.test.ts +2 -5
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +76 -0
- package/src/resources/extensions/gsd/tests/integration/merge-cwd-restore.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/integration/merge-preserve-worktree-on-preteardown-error.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/integration/merge-strategy-regular.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/integration/milestone-transition-worktree.test.ts +5 -7
- package/src/resources/extensions/gsd/tests/integration/workspace-collapse-integration.test.ts +2 -4
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +16 -15
- package/src/resources/extensions/gsd/tests/merge-closeout-consistency-gate.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/merge-db-cycle.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/merge-self-branch-guard.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +91 -0
- package/src/resources/extensions/gsd/tests/milestone-merge-stash-restore.test.ts +439 -0
- package/src/resources/extensions/gsd/tests/milestone-merge-transaction.test.ts +24 -0
- package/src/resources/extensions/gsd/tests/none-mode-gates.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/originalbase-path-comparison.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/parallel-worker-lock-contention.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/preferences-worktree-sync.test.ts +1 -4
- package/src/resources/extensions/gsd/tests/preferences.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +5 -0
- package/src/resources/extensions/gsd/tests/reopen-slice.test.ts +52 -0
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +20 -0
- package/src/resources/extensions/gsd/tests/skill-discovery.test.ts +46 -0
- package/src/resources/extensions/gsd/tests/stale-worktree-cwd.test.ts +3 -5
- package/src/resources/extensions/gsd/tests/stash-pop-gsd-conflict.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/stash-queued-context-files.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/steer-worktree-path.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/system-flat-phase-layout.test.ts +33 -0
- package/src/resources/extensions/gsd/tests/teardown-chdir-failure-clears-registry.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/teardown-cleanup-parity.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/teardown-failure-clears-registry.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +196 -0
- package/src/resources/extensions/gsd/tests/uat-stuck-loop-orphaned-worktree.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/unit-registry.test.ts +26 -1
- package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-db-respawn-truncation.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-db-same-file.test.ts +1 -3
- package/src/resources/extensions/gsd/tests/worktree-expected-warnings.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-git-pathspec.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-main-branch.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-preferences-sync.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-reentry.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +5 -2
- package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +1 -1
- package/src/resources/extensions/gsd/tool-contract.ts +29 -0
- 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/unit-registry.ts +16 -2
- package/src/resources/extensions/gsd/unmerged-milestone-guard.ts +1 -1
- package/src/resources/extensions/gsd/validation-block-guard.ts +2 -1
- package/src/resources/extensions/gsd/workspace-git-preflight.ts +2 -2
- package/src/resources/extensions/gsd/worktree-command.ts +1 -1
- package/src/resources/extensions/gsd/worktree-lifecycle.ts +19 -15
- package/src/resources/extensions/gsd/worktree-reentry.ts +2 -1
- 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/{L2UMqXELiDH3oz-7yguXp → gXoWec4UNqcR0tkuHwYCr}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{L2UMqXELiDH3oz-7yguXp → gXoWec4UNqcR0tkuHwYCr}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// gsd-pi — Pre-merge stash characterization tests.
|
|
2
|
+
|
|
3
|
+
import assert from "node:assert/strict";
|
|
4
|
+
import { execFileSync } from "node:child_process";
|
|
5
|
+
import { existsSync, mkdtempSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
6
|
+
import { tmpdir } from "node:os";
|
|
7
|
+
import { join } from "node:path";
|
|
8
|
+
import { test } from "node:test";
|
|
9
|
+
|
|
10
|
+
import { createPreMergeStash } from "../auto-worktree-merge-stash.ts";
|
|
11
|
+
|
|
12
|
+
function git(cwd: string, args: string[]): string {
|
|
13
|
+
return execFileSync("git", args, {
|
|
14
|
+
cwd,
|
|
15
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
16
|
+
encoding: "utf-8",
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function createRepo(t: { after: (fn: () => void) => void }): string {
|
|
21
|
+
const root = mkdtempSync(join(tmpdir(), "gsd-merge-stash-test-"));
|
|
22
|
+
t.after(() => rmSync(root, { recursive: true, force: true }));
|
|
23
|
+
git(root, ["init"]);
|
|
24
|
+
git(root, ["config", "user.email", "test@example.com"]);
|
|
25
|
+
git(root, ["config", "user.name", "Test User"]);
|
|
26
|
+
writeFileSync(join(root, "tracked.txt"), "base\n");
|
|
27
|
+
git(root, ["add", "."]);
|
|
28
|
+
git(root, ["commit", "-m", "init"]);
|
|
29
|
+
return root;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function status(root: string): string {
|
|
33
|
+
return git(root, ["status", "--porcelain"]);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
test("pre-merge stash moves tracked and untracked local work out of the merge path", (t) => {
|
|
37
|
+
const root = createRepo(t);
|
|
38
|
+
writeFileSync(join(root, "tracked.txt"), "local edit\n");
|
|
39
|
+
writeFileSync(join(root, "untracked.txt"), "local untracked\n");
|
|
40
|
+
|
|
41
|
+
const stash = createPreMergeStash(root, "M001", false);
|
|
42
|
+
stash.stash();
|
|
43
|
+
|
|
44
|
+
assert.equal(status(root), "", "stash leaves the worktree clean for merge");
|
|
45
|
+
assert.ok(!existsSync(join(root, "untracked.txt")), "untracked files are included in the pre-merge stash");
|
|
46
|
+
|
|
47
|
+
stash.restoreForMergeFailure();
|
|
48
|
+
|
|
49
|
+
assert.equal(readFileSync(join(root, "tracked.txt"), "utf8"), "local edit\n");
|
|
50
|
+
assert.equal(readFileSync(join(root, "untracked.txt"), "utf8"), "local untracked\n");
|
|
51
|
+
assert.equal(git(root, ["stash", "list"]).trim(), "", "targeted restore consumes the pre-merge stash");
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test("post-commit stash restore brings local work back after merge commit", (t) => {
|
|
55
|
+
const root = createRepo(t);
|
|
56
|
+
writeFileSync(join(root, "tracked.txt"), "local edit\n");
|
|
57
|
+
writeFileSync(join(root, "untracked.txt"), "local untracked\n");
|
|
58
|
+
|
|
59
|
+
const stash = createPreMergeStash(root, "M002", false);
|
|
60
|
+
stash.stash();
|
|
61
|
+
writeFileSync(join(root, "merged.txt"), "merged\n");
|
|
62
|
+
git(root, ["add", "merged.txt"]);
|
|
63
|
+
git(root, ["commit", "-m", "merge result"]);
|
|
64
|
+
|
|
65
|
+
stash.restoreAfterCommit();
|
|
66
|
+
|
|
67
|
+
assert.equal(readFileSync(join(root, "tracked.txt"), "utf8"), "local edit\n");
|
|
68
|
+
assert.equal(readFileSync(join(root, "untracked.txt"), "utf8"), "local untracked\n");
|
|
69
|
+
assert.equal(git(root, ["stash", "list"]).trim(), "", "post-commit restore consumes the pre-merge stash");
|
|
70
|
+
});
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
// gsd-pi — Milestone directory shelter characterization tests.
|
|
2
|
+
|
|
3
|
+
import assert from "node:assert/strict";
|
|
4
|
+
import { existsSync, mkdtempSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
import { join } from "node:path";
|
|
7
|
+
import { test } from "node:test";
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
createMilestoneDirectoryShelter,
|
|
11
|
+
_setRestoreEntryFnForTests,
|
|
12
|
+
} from "../auto-worktree-milestone-shelter.ts";
|
|
13
|
+
|
|
14
|
+
function createWorkspace(t: { after: (fn: () => void) => void }): string {
|
|
15
|
+
const root = mkdtempSync(join(tmpdir(), "gsd-shelter-test-"));
|
|
16
|
+
t.after(() => rmSync(root, { recursive: true, force: true }));
|
|
17
|
+
mkdirSync(join(root, ".gsd", "milestones", "M001"), { recursive: true });
|
|
18
|
+
mkdirSync(join(root, ".gsd", "milestones", "M002"), { recursive: true });
|
|
19
|
+
writeFileSync(join(root, ".gsd", "milestones", "M001", "CONTEXT.md"), "# target\n");
|
|
20
|
+
writeFileSync(join(root, ".gsd", "milestones", "M002", "CONTEXT.md"), "# queued\n");
|
|
21
|
+
return root;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
test("milestone directory shelter restores queued milestone dirs and cleans the shelter", (t) => {
|
|
25
|
+
const root = createWorkspace(t);
|
|
26
|
+
|
|
27
|
+
const shelter = createMilestoneDirectoryShelter(root, "M001", "Target milestone");
|
|
28
|
+
|
|
29
|
+
assert.ok(existsSync(join(root, ".gsd", "milestones", "M001", "CONTEXT.md")), "target milestone is not sheltered");
|
|
30
|
+
assert.ok(!existsSync(join(root, ".gsd", "milestones", "M002")), "queued milestone is moved out before stash");
|
|
31
|
+
assert.ok(existsSync(join(root, ".gsd", ".milestone-shelter", "M002", "CONTEXT.md")), "queued milestone is recoverable from shelter");
|
|
32
|
+
|
|
33
|
+
shelter.restore();
|
|
34
|
+
shelter.restore();
|
|
35
|
+
|
|
36
|
+
assert.equal(readFileSync(join(root, ".gsd", "milestones", "M002", "CONTEXT.md"), "utf8"), "# queued\n");
|
|
37
|
+
assert.ok(!existsSync(join(root, ".gsd", ".milestone-shelter")), "shelter is removed after successful restore");
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test("milestone directory shelter retains recoverable copy when restore entry fails", (t) => {
|
|
41
|
+
const root = createWorkspace(t);
|
|
42
|
+
const restoreDefault = _setRestoreEntryFnForTests(() => {
|
|
43
|
+
throw new Error("forced restore failure");
|
|
44
|
+
});
|
|
45
|
+
t.after(restoreDefault);
|
|
46
|
+
|
|
47
|
+
const shelter = createMilestoneDirectoryShelter(root, "M001", "Target milestone");
|
|
48
|
+
shelter.restore();
|
|
49
|
+
|
|
50
|
+
assert.ok(!existsSync(join(root, ".gsd", "milestones", "M002", "CONTEXT.md")), "failed restore does not pretend queued files were restored");
|
|
51
|
+
assert.equal(readFileSync(join(root, ".gsd", ".milestone-shelter", "M002", "CONTEXT.md"), "utf8"), "# queued\n");
|
|
52
|
+
});
|
|
@@ -7,15 +7,15 @@ import { join } from "node:path";
|
|
|
7
7
|
import { tmpdir } from "node:os";
|
|
8
8
|
import { execFileSync } from "node:child_process";
|
|
9
9
|
|
|
10
|
+
import { createAutoWorktree } from "../auto-worktree-creation.ts";
|
|
11
|
+
import { enterAutoWorktree } from "../auto-worktree-entry.ts";
|
|
12
|
+
import { mergeMilestoneToMain } from "../auto-worktree-merge.ts";
|
|
10
13
|
import {
|
|
11
|
-
getAutoWorktreeOriginalBase,
|
|
12
14
|
getActiveAutoWorktreeContext,
|
|
15
|
+
getAutoWorktreeOriginalBase,
|
|
13
16
|
_resetAutoWorktreeOriginalBaseForTests,
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
mergeMilestoneToMain,
|
|
17
|
-
teardownAutoWorktree,
|
|
18
|
-
} from "../auto-worktree.ts";
|
|
17
|
+
} from "../auto-worktree-session-registry.ts";
|
|
18
|
+
import { teardownAutoWorktree } from "../auto-worktree-teardown.ts";
|
|
19
19
|
|
|
20
20
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
21
21
|
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import {
|
|
4
|
+
mkdirSync,
|
|
5
|
+
mkdtempSync,
|
|
6
|
+
rmSync,
|
|
7
|
+
writeFileSync,
|
|
8
|
+
} from "node:fs";
|
|
9
|
+
import { tmpdir } from "node:os";
|
|
10
|
+
import { join } from "node:path";
|
|
11
|
+
|
|
12
|
+
import {
|
|
13
|
+
checkResourcesStale,
|
|
14
|
+
readResourceVersion,
|
|
15
|
+
} from "../auto-worktree-resource-version.ts";
|
|
16
|
+
|
|
17
|
+
function withAgentDir<T>(fn: (agentDir: string) => T): T {
|
|
18
|
+
const oldAgentDir = process.env.GSD_CODING_AGENT_DIR;
|
|
19
|
+
const agentDir = mkdtempSync(join(tmpdir(), "gsd-resource-version-"));
|
|
20
|
+
process.env.GSD_CODING_AGENT_DIR = agentDir;
|
|
21
|
+
try {
|
|
22
|
+
return fn(agentDir);
|
|
23
|
+
} finally {
|
|
24
|
+
if (oldAgentDir === undefined) {
|
|
25
|
+
delete process.env.GSD_CODING_AGENT_DIR;
|
|
26
|
+
} else {
|
|
27
|
+
process.env.GSD_CODING_AGENT_DIR = oldAgentDir;
|
|
28
|
+
}
|
|
29
|
+
rmSync(agentDir, { recursive: true, force: true });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
test("readResourceVersion reads gsdVersion from managed-resources manifest", () => {
|
|
34
|
+
withAgentDir((agentDir) => {
|
|
35
|
+
mkdirSync(agentDir, { recursive: true });
|
|
36
|
+
writeFileSync(
|
|
37
|
+
join(agentDir, "managed-resources.json"),
|
|
38
|
+
JSON.stringify({ gsdVersion: "1.2.3", syncedAt: "ignored" }),
|
|
39
|
+
);
|
|
40
|
+
|
|
41
|
+
assert.equal(readResourceVersion(), "1.2.3");
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test("checkResourcesStale reports only when resource version changed", () => {
|
|
46
|
+
withAgentDir((agentDir) => {
|
|
47
|
+
writeFileSync(
|
|
48
|
+
join(agentDir, "managed-resources.json"),
|
|
49
|
+
JSON.stringify({ gsdVersion: "2.0.0" }),
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
assert.equal(checkResourcesStale(null), null);
|
|
53
|
+
assert.equal(checkResourcesStale("2.0.0"), null);
|
|
54
|
+
assert.equal(
|
|
55
|
+
checkResourcesStale("1.9.0"),
|
|
56
|
+
"GSD resources were updated since this session started. Restart gsd to load the new code.",
|
|
57
|
+
);
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -8,7 +8,9 @@ import { existsSync, mkdtempSync, realpathSync, rmSync, writeFileSync } from "no
|
|
|
8
8
|
import { tmpdir } from "node:os";
|
|
9
9
|
import { join } from "node:path";
|
|
10
10
|
|
|
11
|
-
import { createAutoWorktree
|
|
11
|
+
import { createAutoWorktree } from "../auto-worktree-creation.ts";
|
|
12
|
+
import { _resetAutoWorktreeOriginalBaseForTests } from "../auto-worktree-session-registry.ts";
|
|
13
|
+
import { teardownAutoWorktree } from "../auto-worktree-teardown.ts";
|
|
12
14
|
import { classifyProject } from "../detection.ts";
|
|
13
15
|
|
|
14
16
|
function runGit(args: string[], cwd: string): string {
|
|
@@ -697,6 +697,28 @@ test("ensureCodebaseMapFresh: does not rewrite expired metadata when fingerprint
|
|
|
697
697
|
}
|
|
698
698
|
});
|
|
699
699
|
|
|
700
|
+
test("ensureCodebaseMapFresh: stays fresh when stored fileCount exceeds maxFiles", () => {
|
|
701
|
+
const base = makeTmpRepo();
|
|
702
|
+
try {
|
|
703
|
+
for (let i = 0; i < 10; i++) addFile(base, `file${i}.ts`);
|
|
704
|
+
ensureCodebaseMapFresh(base, { maxFiles: 5 }, { ttlMs: 0, force: true });
|
|
705
|
+
|
|
706
|
+
// Simulate a map written by an older version that recorded the full
|
|
707
|
+
// (untruncated) file count in its metadata rather than the capped count.
|
|
708
|
+
const original = readCodebaseMap(base);
|
|
709
|
+
assert.ok(original);
|
|
710
|
+
const fullCountContent = original.replace(/"fileCount":5\b/, '"fileCount":10');
|
|
711
|
+
writeCodebaseMap(base, fullCountContent);
|
|
712
|
+
|
|
713
|
+
const refreshed = ensureCodebaseMapFresh(base, { maxFiles: 5 }, { ttlMs: 0, force: true });
|
|
714
|
+
assert.equal(refreshed.status, "fresh");
|
|
715
|
+
// Timestamp must be preserved so the injected prompt stays byte-identical.
|
|
716
|
+
assert.equal(readCodebaseMap(base), fullCountContent);
|
|
717
|
+
} finally {
|
|
718
|
+
cleanup(base);
|
|
719
|
+
}
|
|
720
|
+
});
|
|
721
|
+
|
|
700
722
|
test("ensureCodebaseMapFresh: uses TTL cache before enumerating files", () => {
|
|
701
723
|
const base = makeTmpRepo();
|
|
702
724
|
try {
|
|
@@ -7,7 +7,7 @@ import assert from "node:assert/strict";
|
|
|
7
7
|
import { readFileSync, mkdtempSync, mkdirSync, writeFileSync, existsSync, cpSync, rmSync } from "node:fs";
|
|
8
8
|
import { join } from "node:path";
|
|
9
9
|
import { tmpdir } from "node:os";
|
|
10
|
-
import { syncStateToProjectRoot } from "../auto-worktree.ts";
|
|
10
|
+
import { syncStateToProjectRoot } from "../auto-worktree-sync.ts";
|
|
11
11
|
|
|
12
12
|
test("#2313: syncStateToProjectRoot copies metrics and completed-units to the project root", () => {
|
|
13
13
|
const root = mkdtempSync(join(tmpdir(), "gsd-sync-metrics-"));
|
|
@@ -1034,6 +1034,10 @@ describe('derive-state-db', async () => {
|
|
|
1034
1034
|
|
|
1035
1035
|
assert.deepStrictEqual(dbState.phase, 'replanning-slice', 'replan-db: phase is replanning-slice');
|
|
1036
1036
|
assert.deepStrictEqual(dbState.phase, fileState.phase, 'replan-db: phase matches filesystem');
|
|
1037
|
+
assert.ok(
|
|
1038
|
+
Object.hasOwn(dbState, 'activeWorkspace'),
|
|
1039
|
+
'replan-db: state preserves explicit activeWorkspace field for auto-loop consumers',
|
|
1040
|
+
);
|
|
1037
1041
|
|
|
1038
1042
|
closeDatabase();
|
|
1039
1043
|
} finally {
|
|
@@ -428,6 +428,52 @@ test("checkEngineHealth repair prunes stale phases artifact rows with present mi
|
|
|
428
428
|
assert.deepEqual(rows.map((row) => row.path), []);
|
|
429
429
|
});
|
|
430
430
|
|
|
431
|
+
test("checkEngineHealth repair keeps phases rows whose own file is still present", async (t) => {
|
|
432
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-doctor-keep-present-phase-artifact-"));
|
|
433
|
+
t.after(() => rmSync(base, { recursive: true, force: true }));
|
|
434
|
+
|
|
435
|
+
const gsdDir = join(base, ".gsd");
|
|
436
|
+
const livePath = "phases/01-m001/01-01-PLAN.md";
|
|
437
|
+
const replacementPath = "milestones/M001/slices/S01/S01-PLAN.md";
|
|
438
|
+
// Both the active phases/ file AND a legacy milestones/ copy exist on disk.
|
|
439
|
+
// The row is "fixable" (a milestones replacement is present), but its own
|
|
440
|
+
// file is present too — repair must not drop the live row.
|
|
441
|
+
mkdirSync(join(gsdDir, "phases", "01-m001"), { recursive: true });
|
|
442
|
+
writeFileSync(join(gsdDir, livePath), "# Plan\n", "utf-8");
|
|
443
|
+
mkdirSync(join(gsdDir, "milestones", "M001", "slices", "S01"), { recursive: true });
|
|
444
|
+
writeFileSync(join(gsdDir, replacementPath), "# Plan\n", "utf-8");
|
|
445
|
+
|
|
446
|
+
openDatabase(join(gsdDir, "gsd.db"));
|
|
447
|
+
insertArtifact({
|
|
448
|
+
path: livePath,
|
|
449
|
+
artifact_type: "PLAN",
|
|
450
|
+
milestone_id: "M001",
|
|
451
|
+
slice_id: "S01",
|
|
452
|
+
task_id: null,
|
|
453
|
+
full_content: "# plan\n",
|
|
454
|
+
});
|
|
455
|
+
|
|
456
|
+
const issues: any[] = [];
|
|
457
|
+
const fixes: string[] = [];
|
|
458
|
+
await checkEngineHealth(base, issues, fixes, { repair: true });
|
|
459
|
+
|
|
460
|
+
assert.equal(
|
|
461
|
+
fixes.some((fix) => fix.includes(livePath)),
|
|
462
|
+
false,
|
|
463
|
+
"repair must not prune a row whose own file is present on disk",
|
|
464
|
+
);
|
|
465
|
+
assert.equal(
|
|
466
|
+
issues.some((issue) => issue.code === "artifact_file_missing" && issue.file === livePath),
|
|
467
|
+
false,
|
|
468
|
+
"a present artifact must not be reported missing",
|
|
469
|
+
);
|
|
470
|
+
|
|
471
|
+
const rows = _getAdapter()!
|
|
472
|
+
.prepare("SELECT path FROM artifacts ORDER BY path")
|
|
473
|
+
.all() as Array<{ path: string }>;
|
|
474
|
+
assert.deepEqual(rows.map((row) => row.path), [livePath]);
|
|
475
|
+
});
|
|
476
|
+
|
|
431
477
|
test("checkEngineHealth repair prunes stale phases task rows against tasks/<T>-<TYPE>.md replacements", async (t) => {
|
|
432
478
|
const base = mkdtempSync(join(tmpdir(), "gsd-doctor-prune-stale-task-phase-artifact-"));
|
|
433
479
|
t.after(() => rmSync(base, { recursive: true, force: true }));
|
|
@@ -540,6 +586,134 @@ test("checkEngineHealth marks escaped phases rows fixable when a milestones repl
|
|
|
540
586
|
assert.equal(issue.fixable, true, "escaped phases rows with a milestones replacement must be marked fixable");
|
|
541
587
|
});
|
|
542
588
|
|
|
589
|
+
test("checkEngineHealth resolves legacy milestones rows through flat-phase files", async (t) => {
|
|
590
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-doctor-flat-fallback-"));
|
|
591
|
+
t.after(() => rmSync(base, { recursive: true, force: true }));
|
|
592
|
+
|
|
593
|
+
const gsdDir = join(base, ".gsd");
|
|
594
|
+
const flatPath = "phases/01-foundation/01-01-PLAN.md";
|
|
595
|
+
const legacyPath = "milestones/M001/slices/S01/S01-PLAN.md";
|
|
596
|
+
mkdirSync(join(gsdDir, "phases", "01-foundation"), { recursive: true });
|
|
597
|
+
writeFileSync(join(gsdDir, flatPath), "# Plan\n", "utf-8");
|
|
598
|
+
|
|
599
|
+
openDatabase(join(gsdDir, "gsd.db"));
|
|
600
|
+
insertArtifact({
|
|
601
|
+
path: legacyPath,
|
|
602
|
+
artifact_type: "PLAN",
|
|
603
|
+
milestone_id: "M001",
|
|
604
|
+
slice_id: "S01",
|
|
605
|
+
task_id: null,
|
|
606
|
+
full_content: "# legacy plan row\n",
|
|
607
|
+
});
|
|
608
|
+
|
|
609
|
+
const issues: any[] = [];
|
|
610
|
+
await checkEngineHealth(base, issues, []);
|
|
611
|
+
|
|
612
|
+
assert.equal(
|
|
613
|
+
issues.some((issue) => issue.code === "artifact_file_missing" && issue.file === legacyPath),
|
|
614
|
+
false,
|
|
615
|
+
"legacy milestones rows should not be reported missing when the flat-phase file exists",
|
|
616
|
+
);
|
|
617
|
+
});
|
|
618
|
+
|
|
619
|
+
test("checkEngineHealth repair prunes stale milestones rows with present flat-phase files", async (t) => {
|
|
620
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-doctor-prune-stale-legacy-artifact-"));
|
|
621
|
+
t.after(() => rmSync(base, { recursive: true, force: true }));
|
|
622
|
+
|
|
623
|
+
const gsdDir = join(base, ".gsd");
|
|
624
|
+
const flatPath = "phases/01-foundation/01-01-PLAN.md";
|
|
625
|
+
const stalePath = "milestones/M001/slices/S01/S01-PLAN.md";
|
|
626
|
+
mkdirSync(join(gsdDir, "phases", "01-foundation"), { recursive: true });
|
|
627
|
+
writeFileSync(join(gsdDir, flatPath), "# Plan\n", "utf-8");
|
|
628
|
+
|
|
629
|
+
openDatabase(join(gsdDir, "gsd.db"));
|
|
630
|
+
insertArtifact({
|
|
631
|
+
path: stalePath,
|
|
632
|
+
artifact_type: "PLAN",
|
|
633
|
+
milestone_id: "M001",
|
|
634
|
+
slice_id: "S01",
|
|
635
|
+
task_id: null,
|
|
636
|
+
full_content: "# stale plan row\n",
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
const issues: any[] = [];
|
|
640
|
+
const fixes: string[] = [];
|
|
641
|
+
await checkEngineHealth(base, issues, fixes, { repair: true });
|
|
642
|
+
|
|
643
|
+
assert.equal(
|
|
644
|
+
issues.some((issue) => issue.code === "artifact_file_missing" && issue.file === stalePath),
|
|
645
|
+
false,
|
|
646
|
+
"repair should not report stale legacy rows it pruned",
|
|
647
|
+
);
|
|
648
|
+
assert.ok(fixes.includes(`pruned stale legacy artifact row ${stalePath}`));
|
|
649
|
+
|
|
650
|
+
const rows = _getAdapter()!
|
|
651
|
+
.prepare("SELECT path FROM artifacts ORDER BY path")
|
|
652
|
+
.all() as Array<{ path: string }>;
|
|
653
|
+
assert.deepEqual(rows.map((row) => row.path), []);
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
test("checkEngineHealth marks stale milestones task rows without flat files fixable", async (t) => {
|
|
657
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-doctor-fixable-stale-legacy-task-artifact-"));
|
|
658
|
+
t.after(() => rmSync(base, { recursive: true, force: true }));
|
|
659
|
+
|
|
660
|
+
const gsdDir = join(base, ".gsd");
|
|
661
|
+
const stalePath = "milestones/M001/slices/S01/tasks/T01-PLAN.md";
|
|
662
|
+
mkdirSync(join(gsdDir, "phases", "01-foundation"), { recursive: true });
|
|
663
|
+
|
|
664
|
+
openDatabase(join(gsdDir, "gsd.db"));
|
|
665
|
+
insertArtifact({
|
|
666
|
+
path: stalePath,
|
|
667
|
+
artifact_type: "PLAN",
|
|
668
|
+
milestone_id: "M001",
|
|
669
|
+
slice_id: "S01",
|
|
670
|
+
task_id: "T01",
|
|
671
|
+
full_content: "# stale task plan row\n",
|
|
672
|
+
});
|
|
673
|
+
|
|
674
|
+
const issues: any[] = [];
|
|
675
|
+
await checkEngineHealth(base, issues, []);
|
|
676
|
+
|
|
677
|
+
const issue = issues.find((candidate) => candidate.code === "artifact_file_missing" && candidate.file === stalePath);
|
|
678
|
+
assert.ok(issue, "missing stale legacy task row should still be reported when repair is off");
|
|
679
|
+
assert.equal(issue.fixable, true, "stale legacy task rows without flat files should be fixable");
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
test("checkEngineHealth repair prunes stale milestones task rows without flat files", async (t) => {
|
|
683
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-doctor-prune-stale-legacy-task-artifact-"));
|
|
684
|
+
t.after(() => rmSync(base, { recursive: true, force: true }));
|
|
685
|
+
|
|
686
|
+
const gsdDir = join(base, ".gsd");
|
|
687
|
+
const stalePath = "milestones/M001/slices/S01/tasks/T01-PLAN.md";
|
|
688
|
+
mkdirSync(join(gsdDir, "phases", "01-foundation"), { recursive: true });
|
|
689
|
+
|
|
690
|
+
openDatabase(join(gsdDir, "gsd.db"));
|
|
691
|
+
insertArtifact({
|
|
692
|
+
path: stalePath,
|
|
693
|
+
artifact_type: "PLAN",
|
|
694
|
+
milestone_id: "M001",
|
|
695
|
+
slice_id: "S01",
|
|
696
|
+
task_id: "T01",
|
|
697
|
+
full_content: "# stale task plan row\n",
|
|
698
|
+
});
|
|
699
|
+
|
|
700
|
+
const issues: any[] = [];
|
|
701
|
+
const fixes: string[] = [];
|
|
702
|
+
await checkEngineHealth(base, issues, fixes, { repair: true });
|
|
703
|
+
|
|
704
|
+
assert.equal(
|
|
705
|
+
issues.some((issue) => issue.code === "artifact_file_missing" && issue.file === stalePath),
|
|
706
|
+
false,
|
|
707
|
+
"repair should prune stale legacy task rows that have no flat file representation",
|
|
708
|
+
);
|
|
709
|
+
assert.ok(fixes.includes(`pruned stale legacy artifact row ${stalePath}`));
|
|
710
|
+
|
|
711
|
+
const rows = _getAdapter()!
|
|
712
|
+
.prepare("SELECT path FROM artifacts ORDER BY path")
|
|
713
|
+
.all() as Array<{ path: string }>;
|
|
714
|
+
assert.deepEqual(rows.map((row) => row.path), []);
|
|
715
|
+
});
|
|
716
|
+
|
|
543
717
|
function taskSummary(id: string, verificationResult = "passed"): string {
|
|
544
718
|
return [
|
|
545
719
|
"---",
|
|
@@ -13,7 +13,7 @@ import { basename, join } from "node:path";
|
|
|
13
13
|
import {
|
|
14
14
|
fastForwardReusedMilestoneBranchIfSafe,
|
|
15
15
|
_isBranchCheckedOutElsewhere,
|
|
16
|
-
} from "../auto-worktree.js";
|
|
16
|
+
} from "../auto-worktree-branch-lifecycle.js";
|
|
17
17
|
|
|
18
18
|
const NO_PROMPT_ENV = {
|
|
19
19
|
...process.env,
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
needsFlatPhaseMigration,
|
|
14
14
|
pruneStaleFlatPhaseBackups,
|
|
15
15
|
} from "../flat-phase-migration.ts";
|
|
16
|
-
import { openDatabase, closeDatabase, insertMilestone, insertSlice, insertTask, getAllMilestones, getMilestoneSlices, getSliceTasks } from "../gsd-db.ts";
|
|
16
|
+
import { openDatabase, closeDatabase, insertArtifact, insertMilestone, insertSlice, insertTask, getAllMilestones, getMilestoneSlices, getSliceTasks, _getAdapter } from "../gsd-db.ts";
|
|
17
17
|
|
|
18
18
|
const tmpDirs: string[] = [];
|
|
19
19
|
function makeTmp(options: { withTask?: boolean } = {}): string {
|
|
@@ -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;
|
|
@@ -120,6 +147,49 @@ test("migrateToFlatPhase preserves milestone/slice/task counts in DB", async ()
|
|
|
120
147
|
assert.equal(getSliceTasks("M001", "S01").length, tasksBefore);
|
|
121
148
|
});
|
|
122
149
|
|
|
150
|
+
test("migrateToFlatPhase prunes legacy milestones artifact rows after flat render", async () => {
|
|
151
|
+
const base = makeTmp();
|
|
152
|
+
insertArtifact({
|
|
153
|
+
path: "milestones/M001/M001-ROADMAP.md",
|
|
154
|
+
artifact_type: "ROADMAP",
|
|
155
|
+
milestone_id: "M001",
|
|
156
|
+
slice_id: null,
|
|
157
|
+
task_id: null,
|
|
158
|
+
full_content: "# M001: Foundation\n",
|
|
159
|
+
});
|
|
160
|
+
insertArtifact({
|
|
161
|
+
path: "milestones/M001/slices/S01/S01-PLAN.md",
|
|
162
|
+
artifact_type: "PLAN",
|
|
163
|
+
milestone_id: "M001",
|
|
164
|
+
slice_id: "S01",
|
|
165
|
+
task_id: null,
|
|
166
|
+
full_content: "# Plan\n",
|
|
167
|
+
});
|
|
168
|
+
insertArtifact({
|
|
169
|
+
path: "milestones/M001/slices/S01/tasks/T01-PLAN.md",
|
|
170
|
+
artifact_type: "PLAN",
|
|
171
|
+
milestone_id: "M001",
|
|
172
|
+
slice_id: "S01",
|
|
173
|
+
task_id: "T01",
|
|
174
|
+
full_content: "# Task Plan\n",
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
await migrateToFlatPhase(base);
|
|
178
|
+
|
|
179
|
+
const rows = _getAdapter()!
|
|
180
|
+
.prepare("SELECT path FROM artifacts ORDER BY path")
|
|
181
|
+
.all() as Array<{ path: string }>;
|
|
182
|
+
assert.equal(
|
|
183
|
+
rows.some((row) => row.path.startsWith("milestones/")),
|
|
184
|
+
false,
|
|
185
|
+
"legacy milestones/ artifact rows should be pruned after successful flat migration",
|
|
186
|
+
);
|
|
187
|
+
assert.ok(
|
|
188
|
+
rows.some((row) => row.path.startsWith("phases/")),
|
|
189
|
+
"flat-phase render should leave replacement projection rows in the artifacts table",
|
|
190
|
+
);
|
|
191
|
+
});
|
|
192
|
+
|
|
123
193
|
test("migrateToFlatPhase is idempotent (second run is a no-op)", async () => {
|
|
124
194
|
const base = makeTmp();
|
|
125
195
|
await migrateToFlatPhase(base);
|
|
@@ -7,7 +7,8 @@ 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 } from "../markdown-renderer.ts";
|
|
10
|
+
import { renderPlanFromDb, renderRoadmapFromDb, renderAssessmentFromDb } from "../markdown-renderer.ts";
|
|
11
|
+
import { clearPathCache } from "../paths.ts";
|
|
11
12
|
import { openDatabase, closeDatabase, insertMilestone, insertSlice, insertTask } from "../gsd-db.ts";
|
|
12
13
|
|
|
13
14
|
const tmpDirs: string[] = [];
|
|
@@ -81,3 +82,26 @@ test("renderPlanFromDb does NOT write per-task plan files", async () => {
|
|
|
81
82
|
// taskPlanPaths should be empty — no per-task files written
|
|
82
83
|
assert.equal(result.taskPlanPaths.length, 0);
|
|
83
84
|
});
|
|
85
|
+
|
|
86
|
+
// Regression for #1190: a stray slices/SID/ subdir inside a flat-phase milestone
|
|
87
|
+
// must not divert the ASSESSMENT write into slices/SID/ (a hybrid path that
|
|
88
|
+
// auto-mode verification cannot find). It must land at the phase root.
|
|
89
|
+
test("renderAssessmentFromDb 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
|
+
|
|
94
|
+
// Simulate the anomaly: an earlier planning step created slices/S01/ inside
|
|
95
|
+
// the flat-phase dir.
|
|
96
|
+
mkdirSync(join(phaseDir, "slices", "S01"), { recursive: true });
|
|
97
|
+
clearPathCache();
|
|
98
|
+
|
|
99
|
+
const result = await renderAssessmentFromDb(base, "M001", "S01", {
|
|
100
|
+
verdict: "on-track",
|
|
101
|
+
assessment: "Roadmap is on track.",
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
assert.match(result.assessmentPath, /phases[/\\]01-[^/\\]+[/\\]01-01-ASSESSMENT\.md$/);
|
|
105
|
+
assert.ok(!result.assessmentPath.includes(join("slices", "S01")), "must not write under slices/S01/");
|
|
106
|
+
assert.ok(existsSync(result.assessmentPath), "assessment file should exist at the phase root");
|
|
107
|
+
});
|
|
@@ -33,6 +33,10 @@ import {
|
|
|
33
33
|
getMilestoneSliceSummaries,
|
|
34
34
|
getClosedSliceIds,
|
|
35
35
|
getActiveMilestoneFromDb,
|
|
36
|
+
getActiveSliceFromDb,
|
|
37
|
+
getActiveTaskFromDb,
|
|
38
|
+
getActiveTaskIdFromDb,
|
|
39
|
+
getSliceTaskCounts,
|
|
36
40
|
deleteMilestone,
|
|
37
41
|
clearEngineHierarchy,
|
|
38
42
|
recordMilestoneCommitAttribution,
|
|
@@ -240,6 +244,42 @@ describe('gsd-db', () => {
|
|
|
240
244
|
closeDatabase();
|
|
241
245
|
});
|
|
242
246
|
|
|
247
|
+
test("gsd-db: active readers use the shared closed-status vocabulary", () => {
|
|
248
|
+
openDatabase(":memory:");
|
|
249
|
+
|
|
250
|
+
try {
|
|
251
|
+
insertMilestone({ id: "M001", title: "Status vocabulary", status: "active" });
|
|
252
|
+
insertSlice({ id: "S01", milestoneId: "M001", title: "Closed dep", status: "closed", sequence: 1 });
|
|
253
|
+
insertSlice({ id: "S02", milestoneId: "M001", title: "Runnable", status: "pending", depends: ["S01"], sequence: 2 });
|
|
254
|
+
insertTask({ id: "T01", sliceId: "S02", milestoneId: "M001", title: "Skipped", status: "skipped", sequence: 1 });
|
|
255
|
+
insertTask({ id: "T02", sliceId: "S02", milestoneId: "M001", title: "Closed", status: "closed", sequence: 2 });
|
|
256
|
+
insertTask({ id: "T03", sliceId: "S02", milestoneId: "M001", title: "Pending", status: "pending", sequence: 3 });
|
|
257
|
+
|
|
258
|
+
assert.equal(
|
|
259
|
+
getActiveSliceFromDb("M001")?.id,
|
|
260
|
+
"S02",
|
|
261
|
+
"closed dependency slices must satisfy dependencies and be excluded from active slice selection",
|
|
262
|
+
);
|
|
263
|
+
assert.equal(
|
|
264
|
+
getActiveTaskFromDb("M001", "S02")?.id,
|
|
265
|
+
"T03",
|
|
266
|
+
"skipped/closed tasks must be excluded from active task selection",
|
|
267
|
+
);
|
|
268
|
+
assert.equal(
|
|
269
|
+
getActiveTaskIdFromDb("M001", "S02")?.id,
|
|
270
|
+
"T03",
|
|
271
|
+
"lightweight active task selection must use the same closed-status vocabulary",
|
|
272
|
+
);
|
|
273
|
+
assert.deepEqual(
|
|
274
|
+
getSliceTaskCounts("M001", "S02"),
|
|
275
|
+
{ total: 3, done: 2, pending: 1 },
|
|
276
|
+
"slice task counts must derive done/pending from the shared closed-status vocabulary",
|
|
277
|
+
);
|
|
278
|
+
} finally {
|
|
279
|
+
closeDatabase();
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
|
|
243
283
|
test('gsd-db: active_decisions view excludes superseded', () => {
|
|
244
284
|
openDatabase(':memory:');
|
|
245
285
|
|