@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
|
@@ -797,9 +797,11 @@ test("stuck-loop: empty ring on a freshly constructed orchestrator advances norm
|
|
|
797
797
|
assert.equal(result.kind, "advanced");
|
|
798
798
|
});
|
|
799
799
|
|
|
800
|
-
test("stuck-loop:
|
|
800
|
+
test("stuck-loop: distinct units making forward progress do not block", async (t) => {
|
|
801
|
+
// Healthy forward progress visits new units every advance — no key repeats,
|
|
802
|
+
// so no oscillation/repeat rule fires even once the window is full.
|
|
801
803
|
let i = 0;
|
|
802
|
-
const sequence = ["M001/S01/A", "M001/S01/B", "M001/S01/
|
|
804
|
+
const sequence = ["M001/S01/A", "M001/S01/B", "M001/S01/C", "M001/S01/D", "M001/S01/E", "M001/S01/F"];
|
|
803
805
|
const f = makeFixture({
|
|
804
806
|
dispatch: () => ({ action: "dispatch", unitType: "execute-task", unitId: sequence[i++ % sequence.length], prompt: "p" }),
|
|
805
807
|
});
|
|
@@ -811,6 +813,43 @@ test("stuck-loop: partial fill of mixed units does not block", async (t) => {
|
|
|
811
813
|
}
|
|
812
814
|
});
|
|
813
815
|
|
|
816
|
+
test("stuck-loop #1225: two-unit oscillation blocks once the window saturates", async (t) => {
|
|
817
|
+
// Reproduces the execute-task ↔ complete-slice loop: a slice gate keeps
|
|
818
|
+
// reopening the same task, so two keys alternate forever. Neither key ever
|
|
819
|
+
// saturates the window on its own, so the old matchingCount>=SIZE gate never
|
|
820
|
+
// consulted detect-stuck and the loop ran until an external kill. The window
|
|
821
|
+
// still fills, so detect-stuck's oscillation/repeat rules must now fire.
|
|
822
|
+
let i = 0;
|
|
823
|
+
const sequence: Array<{ unitType: string; unitId: string }> = [
|
|
824
|
+
{ unitType: "execute-task", unitId: "M001/S01/T01" },
|
|
825
|
+
{ unitType: "complete-slice", unitId: "M001/S01" },
|
|
826
|
+
];
|
|
827
|
+
const f = makeFixture({
|
|
828
|
+
dispatch: () => {
|
|
829
|
+
const next = sequence[i++ % sequence.length];
|
|
830
|
+
return { action: "dispatch", unitType: next.unitType, unitId: next.unitId, prompt: "p" };
|
|
831
|
+
},
|
|
832
|
+
});
|
|
833
|
+
t.after(() => f.cleanup());
|
|
834
|
+
|
|
835
|
+
let blocked: Awaited<ReturnType<typeof f.orchestrator.advance>> | undefined;
|
|
836
|
+
for (let round = 0; round < STUCK_WINDOW_SIZE; round++) {
|
|
837
|
+
const result = await f.orchestrator.advance();
|
|
838
|
+
if (result.kind === "blocked") {
|
|
839
|
+
blocked = result;
|
|
840
|
+
break;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
assert.ok(blocked, "oscillation must be detected once the window saturates");
|
|
845
|
+
if (!blocked || blocked.kind !== "blocked") return;
|
|
846
|
+
assert.equal(blocked.action, "stop");
|
|
847
|
+
assert.ok(
|
|
848
|
+
blocked.reason.startsWith("stuck-loop:"),
|
|
849
|
+
`expected stuck-loop verdict, got: ${blocked.reason}`,
|
|
850
|
+
);
|
|
851
|
+
});
|
|
852
|
+
|
|
814
853
|
test("stuck-loop: ring saturated with same unit blocks with action 'stop' and stuck-loop reason", async (t) => {
|
|
815
854
|
const f = makeFixture();
|
|
816
855
|
t.after(() => f.cleanup());
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import test from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
|
-
import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync } from "node:fs";
|
|
3
|
+
import { existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
import {
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
maybeWriteParallelResearchCostSpikeBlocker,
|
|
9
9
|
} from "../auto-post-unit.ts";
|
|
10
10
|
import { resolveExpectedArtifactPath } from "../auto-recovery.ts";
|
|
11
|
+
import { _clearGsdRootCache, clearPathCache } from "../paths.ts";
|
|
11
12
|
|
|
12
13
|
test("missing execute-task artifact includes completion contract and completion-tool hint", () => {
|
|
13
14
|
const base = mkdtempSync(join(tmpdir(), "gsd-artifact-diag-"));
|
|
@@ -56,3 +57,49 @@ test("parallel research cost spike writes durable PARALLEL-BLOCKER", () => {
|
|
|
56
57
|
rmSync(base, { recursive: true, force: true });
|
|
57
58
|
}
|
|
58
59
|
});
|
|
60
|
+
|
|
61
|
+
// ── #1238: symlinked project root must not produce a ../ path walk ────────────
|
|
62
|
+
//
|
|
63
|
+
// On WSL (and any layout where the project root is reached through a symlink),
|
|
64
|
+
// artifact resolution canonicalizes through gsdRoot (realpathSync), so the
|
|
65
|
+
// resolved artifact path lives under the REAL root while `basePath` is still the
|
|
66
|
+
// symlink. Before the fix, relative(symlinkBase, realArtifactPath) walked up out
|
|
67
|
+
// of the symlink (../../…/home/…) and the diagnostic reported that broken path as
|
|
68
|
+
// "not found on disk". Normalizing basePath to its real path first keeps the
|
|
69
|
+
// displayed path anchored at the real .gsd root.
|
|
70
|
+
|
|
71
|
+
test("symlinked project root yields a clean .gsd-relative artifact path, not a ../ walk (#1238)", () => {
|
|
72
|
+
const realRoot = realpathSync(mkdtempSync(join(tmpdir(), "gsd-symlink-real-")));
|
|
73
|
+
const linkParent = realpathSync(mkdtempSync(join(tmpdir(), "gsd-symlink-link-")));
|
|
74
|
+
const linkRoot = join(linkParent, "project");
|
|
75
|
+
try {
|
|
76
|
+
// Content-bearing legacy milestone dir so plan-milestone resolves the
|
|
77
|
+
// canonical (realpath) ROADMAP path under realRoot/.gsd/milestones/M001/.
|
|
78
|
+
const milestoneDir = join(realRoot, ".gsd", "milestones", "M001");
|
|
79
|
+
mkdirSync(milestoneDir, { recursive: true });
|
|
80
|
+
writeFileSync(join(milestoneDir, "M001-CONTEXT.md"), "# context\n");
|
|
81
|
+
// The ROADMAP intentionally does not exist — this hits the "not found on
|
|
82
|
+
// disk" branch, the exact symptom in #1238.
|
|
83
|
+
symlinkSync(realRoot, linkRoot);
|
|
84
|
+
|
|
85
|
+
_clearGsdRootCache();
|
|
86
|
+
clearPathCache();
|
|
87
|
+
|
|
88
|
+
const msg = _describeArtifactVerificationFailureForTest("plan-milestone", "M001", linkRoot);
|
|
89
|
+
|
|
90
|
+
assert.match(msg, /was not found on disk after unit execution/);
|
|
91
|
+
// The displayed path must be anchored at the real .gsd root, not a broken
|
|
92
|
+
// parent-walk out of the symlinked base.
|
|
93
|
+
assert.doesNotMatch(msg, /\.\.[/\\]/, "must not contain a ../ walk out of the symlinked base");
|
|
94
|
+
assert.match(
|
|
95
|
+
msg,
|
|
96
|
+
/\.gsd[/\\]milestones[/\\]M001[/\\]M001-ROADMAP\.md/,
|
|
97
|
+
"must show the clean .gsd-relative ROADMAP path",
|
|
98
|
+
);
|
|
99
|
+
} finally {
|
|
100
|
+
_clearGsdRootCache();
|
|
101
|
+
clearPathCache();
|
|
102
|
+
rmSync(linkParent, { recursive: true, force: true });
|
|
103
|
+
rmSync(realRoot, { recursive: true, force: true });
|
|
104
|
+
}
|
|
105
|
+
});
|
|
@@ -299,6 +299,63 @@ test("refreshRecoveryDbForArtifact treats missing execute-task DB rows as fatal
|
|
|
299
299
|
});
|
|
300
300
|
});
|
|
301
301
|
|
|
302
|
+
test("refreshRecoveryDbForArtifact promotes an open execute-task DB row when artifacts are verified on disk (#1204)", () => {
|
|
303
|
+
const dir = makeTmpProject();
|
|
304
|
+
insertTask({
|
|
305
|
+
milestoneId: "M001",
|
|
306
|
+
sliceId: "S01",
|
|
307
|
+
id: "T01",
|
|
308
|
+
title: "Stuck Task",
|
|
309
|
+
status: "pending",
|
|
310
|
+
});
|
|
311
|
+
const sliceDir = join(dir, ".gsd", "milestones", "M001", "slices", "S01");
|
|
312
|
+
mkdirSync(sliceDir, { recursive: true });
|
|
313
|
+
writeFileSync(join(sliceDir, "S01-PLAN.md"), [
|
|
314
|
+
"# S01 Plan",
|
|
315
|
+
"",
|
|
316
|
+
"- [ ] **T01: Implement feature** `est:1h`",
|
|
317
|
+
"",
|
|
318
|
+
].join("\n"));
|
|
319
|
+
|
|
320
|
+
const result = refreshRecoveryDbForArtifact("execute-task", "M001/S01/T01", dir);
|
|
321
|
+
|
|
322
|
+
assert.deepEqual(result, { ok: true });
|
|
323
|
+
assert.equal(getTask("M001", "S01", "T01")?.status, "complete");
|
|
324
|
+
const planContent = readFileSync(join(sliceDir, "S01-PLAN.md"), "utf-8");
|
|
325
|
+
assert.ok(planContent.includes("[x] **T01:"), "plan checkbox should be re-rendered to [x]");
|
|
326
|
+
const events = readEvents(join(dir, ".gsd", "event-log.jsonl"));
|
|
327
|
+
assert.ok(
|
|
328
|
+
events.some((e) => e.cmd === "complete-task" && e.trigger_reason === "stuck-artifact-recovery"),
|
|
329
|
+
"a complete-task recovery event should be appended",
|
|
330
|
+
);
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
test("refreshRecoveryDbForArtifact still appends the recovery event when the plan checkbox sync throws after DB promotion (#1221)", () => {
|
|
334
|
+
const dir = makeTmpProject();
|
|
335
|
+
insertTask({
|
|
336
|
+
milestoneId: "M001",
|
|
337
|
+
sliceId: "S01",
|
|
338
|
+
id: "T01",
|
|
339
|
+
title: "Stuck Task",
|
|
340
|
+
status: "pending",
|
|
341
|
+
});
|
|
342
|
+
const sliceDir = join(dir, ".gsd", "milestones", "M001", "slices", "S01");
|
|
343
|
+
mkdirSync(sliceDir, { recursive: true });
|
|
344
|
+
// Make the expected plan path a directory so the best-effort checkbox
|
|
345
|
+
// rewrite (readFileSync) throws EISDIR after the DB row is already promoted.
|
|
346
|
+
mkdirSync(join(sliceDir, "S01-PLAN.md"), { recursive: true });
|
|
347
|
+
|
|
348
|
+
const result = refreshRecoveryDbForArtifact("execute-task", "M001/S01/T01", dir);
|
|
349
|
+
|
|
350
|
+
assert.deepEqual(result, { ok: true });
|
|
351
|
+
assert.equal(getTask("M001", "S01", "T01")?.status, "complete");
|
|
352
|
+
const events = readEvents(join(dir, ".gsd", "event-log.jsonl"));
|
|
353
|
+
assert.ok(
|
|
354
|
+
events.some((e) => e.cmd === "complete-task" && e.trigger_reason === "stuck-artifact-recovery"),
|
|
355
|
+
"recovery event must still be appended even when the plan checkbox sync fails",
|
|
356
|
+
);
|
|
357
|
+
});
|
|
358
|
+
|
|
302
359
|
test("refreshRecoveryDbForArtifact closes complete-milestone DB row when artifacts exist but DB is stale (#5568)", async () => {
|
|
303
360
|
const base = mkdtempSync(join(tmpdir(), "auto-recovery-complete-ms-"));
|
|
304
361
|
mkdirSync(join(base, ".gsd"), { recursive: true });
|
|
@@ -4,7 +4,7 @@ import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
|
|
7
|
-
import { closeDatabase, getAllMilestones, getMilestone, insertMilestone, isDbAvailable, openDatabase } from "../gsd-db.ts";
|
|
7
|
+
import { _getAdapter, closeDatabase, getAllMilestones, getMilestone, insertMilestone, isDbAvailable, openDatabase } from "../gsd-db.ts";
|
|
8
8
|
import { reconcileMergedMilestonesFromJournal, reconcileProjectMilestonesFromDisk } from "../auto-start.ts";
|
|
9
9
|
import { emitWorktreeMerged } from "../worktree-telemetry.ts";
|
|
10
10
|
|
|
@@ -69,3 +69,31 @@ test("#5389: bootstrap reconciles PROJECT.md milestones that are missing from DB
|
|
|
69
69
|
rmSync(base, { recursive: true, force: true });
|
|
70
70
|
}
|
|
71
71
|
});
|
|
72
|
+
|
|
73
|
+
test("#1236: bootstrap merged-milestone reconciliation degrades to a warning instead of aborting when the DB is degraded", () => {
|
|
74
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-merged-reconcile-degraded-"));
|
|
75
|
+
try {
|
|
76
|
+
mkdirSync(join(base, ".gsd"), { recursive: true });
|
|
77
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
78
|
+
insertMilestone({ id: "M001", title: "Merged Milestone", status: "active" });
|
|
79
|
+
|
|
80
|
+
emitWorktreeMerged(base, "M001", { reason: "milestone-complete", conflict: false });
|
|
81
|
+
|
|
82
|
+
// Simulate a degraded DB: the connection stays "available" (isDbAvailable()
|
|
83
|
+
// remains true because the handle is non-null), but the milestones table is
|
|
84
|
+
// gone, so the reconciler's DB access throws partway through bootstrap.
|
|
85
|
+
_getAdapter()!.exec("DROP TABLE milestones");
|
|
86
|
+
assert.equal(isDbAvailable(), true);
|
|
87
|
+
|
|
88
|
+
// Regression (#1236): this reconciler was previously unguarded, so a
|
|
89
|
+
// degraded-DB failure threw and aborted the rest of `/gsd auto` bootstrap.
|
|
90
|
+
// It must now catch, warn, and return 0, matching its sibling
|
|
91
|
+
// reconcileProjectMilestonesFromDisk. Reaching the assertion proves it did
|
|
92
|
+
// not throw.
|
|
93
|
+
const closed = reconcileMergedMilestonesFromJournal(base);
|
|
94
|
+
assert.equal(closed, 0);
|
|
95
|
+
} finally {
|
|
96
|
+
if (isDbAvailable()) closeDatabase();
|
|
97
|
+
rmSync(base, { recursive: true, force: true });
|
|
98
|
+
}
|
|
99
|
+
});
|
package/src/resources/extensions/gsd/tests/auto-timeout-recovery-supervisor-inflight-guard.test.ts
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Idle/hard timeout recovery honors the in-flight-tool guard (#1229 follow-up).
|
|
3
|
+
*
|
|
4
|
+
* `recoverTimedOutUnit` steers a still-running unit with `deliverAs: "steer"`
|
|
5
|
+
* and, before that, may swap the session model via
|
|
6
|
+
* `applySupervisorModelIfConfigured`. Swapping the model and triggering a turn
|
|
7
|
+
* while non-interactive tool calls are still open skips tool results and trips
|
|
8
|
+
* provider errors (#3512) — the same failure the soft wrap-up and continue-here
|
|
9
|
+
* timers guard against. This test pins that the idle and hard steering branches
|
|
10
|
+
* gate both the model swap and `triggerTurn` on `getInFlightToolCount() === 0`.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import test from "node:test";
|
|
14
|
+
import assert from "node:assert/strict";
|
|
15
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
16
|
+
import { join } from "node:path";
|
|
17
|
+
import { tmpdir } from "node:os";
|
|
18
|
+
|
|
19
|
+
import { recoverTimedOutUnit, type RecoveryContext } from "../auto-timeout-recovery.ts";
|
|
20
|
+
import { clearGSDPreferencesCache } from "../preferences.ts";
|
|
21
|
+
import {
|
|
22
|
+
markToolStart,
|
|
23
|
+
markToolEnd,
|
|
24
|
+
clearInFlightTools,
|
|
25
|
+
} from "../auto-tool-tracking.ts";
|
|
26
|
+
|
|
27
|
+
const SUPERVISOR_PREFS = [
|
|
28
|
+
"auto_supervisor:",
|
|
29
|
+
" soft_timeout_minutes: 1",
|
|
30
|
+
" idle_timeout_minutes: 100",
|
|
31
|
+
" hard_timeout_minutes: 100",
|
|
32
|
+
" model:",
|
|
33
|
+
" model: supervisor-primary",
|
|
34
|
+
" fallbacks: [supervisor-fb]",
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
interface Harness {
|
|
38
|
+
home: string;
|
|
39
|
+
base: string;
|
|
40
|
+
ctx: any;
|
|
41
|
+
pi: any;
|
|
42
|
+
rctx: RecoveryContext;
|
|
43
|
+
setModelCalls: unknown[][];
|
|
44
|
+
sendMessageCalls: unknown[][];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function makeHarness(): Harness {
|
|
48
|
+
const home = mkdtempSync(join(tmpdir(), "gsd-recovery-guard-home-"));
|
|
49
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-recovery-guard-base-"));
|
|
50
|
+
process.env.GSD_HOME = home;
|
|
51
|
+
writeFileSync(join(home, "preferences.md"), ["---", ...SUPERVISOR_PREFS, "---", ""].join("\n"));
|
|
52
|
+
clearGSDPreferencesCache();
|
|
53
|
+
|
|
54
|
+
const setModelCalls: unknown[][] = [];
|
|
55
|
+
const sendMessageCalls: unknown[][] = [];
|
|
56
|
+
|
|
57
|
+
const ctx = {
|
|
58
|
+
ui: { notify: () => {} },
|
|
59
|
+
model: { provider: "anthropic" },
|
|
60
|
+
modelRegistry: {
|
|
61
|
+
getAvailable: () => [
|
|
62
|
+
{ id: "supervisor-primary", provider: "anthropic" },
|
|
63
|
+
{ id: "supervisor-fb", provider: "anthropic" },
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
} as any;
|
|
67
|
+
|
|
68
|
+
const pi = {
|
|
69
|
+
sendMessage: (...args: unknown[]) => {
|
|
70
|
+
sendMessageCalls.push(args);
|
|
71
|
+
},
|
|
72
|
+
setModel: async (...args: unknown[]) => {
|
|
73
|
+
setModelCalls.push(args);
|
|
74
|
+
return true;
|
|
75
|
+
},
|
|
76
|
+
getThinkingLevel: () => "off",
|
|
77
|
+
setThinkingLevel: () => {},
|
|
78
|
+
} as any;
|
|
79
|
+
|
|
80
|
+
const rctx: RecoveryContext = {
|
|
81
|
+
basePath: base,
|
|
82
|
+
verbose: false,
|
|
83
|
+
currentUnitStartedAt: 1234,
|
|
84
|
+
unitRecoveryCount: new Map(),
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
return { home, base, ctx, pi, rctx, setModelCalls, sendMessageCalls };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function cleanup(h: Harness): void {
|
|
91
|
+
clearInFlightTools();
|
|
92
|
+
clearGSDPreferencesCache();
|
|
93
|
+
delete process.env.GSD_HOME;
|
|
94
|
+
rmSync(h.home, { recursive: true, force: true });
|
|
95
|
+
rmSync(h.base, { recursive: true, force: true });
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// "discuss-project" resolves to a PROJECT.md that never exists in the temp base,
|
|
99
|
+
// so recovery skips the "already on disk" shortcut and reaches the steering
|
|
100
|
+
// branch with recoveryAttempts (0) < maxRecoveryAttempts.
|
|
101
|
+
for (const reason of ["idle", "hard"] as const) {
|
|
102
|
+
test(`${reason} recovery skips the supervisor model swap while a tool is in flight`, async () => {
|
|
103
|
+
const h = makeHarness();
|
|
104
|
+
try {
|
|
105
|
+
markToolStart("call-1", true, "some_tool");
|
|
106
|
+
|
|
107
|
+
const outcome = await recoverTimedOutUnit(
|
|
108
|
+
h.ctx,
|
|
109
|
+
h.pi,
|
|
110
|
+
"discuss-project",
|
|
111
|
+
"P01",
|
|
112
|
+
reason,
|
|
113
|
+
h.rctx,
|
|
114
|
+
);
|
|
115
|
+
|
|
116
|
+
assert.equal(outcome, "recovered");
|
|
117
|
+
assert.equal(
|
|
118
|
+
h.setModelCalls.length,
|
|
119
|
+
0,
|
|
120
|
+
"model must NOT swap while a tool is in flight",
|
|
121
|
+
);
|
|
122
|
+
assert.equal(h.sendMessageCalls.length, 1, "steer message still sent (queued)");
|
|
123
|
+
const opts = h.sendMessageCalls[0][1] as { triggerTurn?: boolean; deliverAs?: string };
|
|
124
|
+
assert.equal(opts.triggerTurn, false, "turn is not triggered mid-tool-call");
|
|
125
|
+
assert.equal(opts.deliverAs, "steer");
|
|
126
|
+
|
|
127
|
+
markToolEnd("call-1");
|
|
128
|
+
} finally {
|
|
129
|
+
cleanup(h);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test(`${reason} recovery applies the supervisor model swap when no tools are in flight`, async () => {
|
|
134
|
+
const h = makeHarness();
|
|
135
|
+
try {
|
|
136
|
+
const outcome = await recoverTimedOutUnit(
|
|
137
|
+
h.ctx,
|
|
138
|
+
h.pi,
|
|
139
|
+
"discuss-project",
|
|
140
|
+
"P01",
|
|
141
|
+
reason,
|
|
142
|
+
h.rctx,
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
assert.equal(outcome, "recovered");
|
|
146
|
+
assert.equal(h.setModelCalls.length, 1, "model swaps to the supervisor model");
|
|
147
|
+
assert.deepEqual(
|
|
148
|
+
h.setModelCalls[0][0],
|
|
149
|
+
{ id: "supervisor-primary", provider: "anthropic" },
|
|
150
|
+
"swaps to the configured supervisor primary",
|
|
151
|
+
);
|
|
152
|
+
assert.equal(h.sendMessageCalls.length, 1);
|
|
153
|
+
const opts = h.sendMessageCalls[0][1] as { triggerTurn?: boolean };
|
|
154
|
+
assert.equal(opts.triggerTurn, true, "turn is triggered when no tools are open");
|
|
155
|
+
} finally {
|
|
156
|
+
cleanup(h);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
}
|
|
@@ -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 {
|
|
@@ -18,6 +18,7 @@ import { randomUUID } from "node:crypto";
|
|
|
18
18
|
import { handleVerdict, parseValidationFile } from "../commands-verdict.ts";
|
|
19
19
|
import { openDatabase, closeDatabase, _getAdapter, insertAssessment } from "../gsd-db.ts";
|
|
20
20
|
import { invalidateStateCache } from "../state.ts";
|
|
21
|
+
import { resolveMilestoneFile } from "../paths.ts";
|
|
21
22
|
|
|
22
23
|
interface NotifyCall {
|
|
23
24
|
message: string;
|
|
@@ -363,7 +364,8 @@ test("handleVerdict pass override works when validation only exists in DB", asyn
|
|
|
363
364
|
const { ctx, calls } = makeMockCtx();
|
|
364
365
|
await handleVerdict('pass --milestone M001 --rationale "reviewed and accepted"', ctx, base);
|
|
365
366
|
|
|
366
|
-
const validationPath =
|
|
367
|
+
const validationPath = resolveMilestoneFile(base, "M001", "VALIDATION");
|
|
368
|
+
assert.ok(validationPath, "DB-backed validation should be materialized to disk");
|
|
367
369
|
const rewritten = readFileSync(validationPath, "utf-8");
|
|
368
370
|
assert.match(rewritten, /^verdict: pass$/m, "DB-backed validation should be rewritten as pass");
|
|
369
371
|
assert.match(rewritten, /reviewed and accepted/, "explicit rationale should be persisted");
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
writeCompatMarker,
|
|
13
13
|
normalizeForHash,
|
|
14
14
|
computeProjectionSha,
|
|
15
|
+
pruneOrphanedProjectionEntries,
|
|
15
16
|
EMPTY_MARKER,
|
|
16
17
|
compatMarkerPath,
|
|
17
18
|
} from "../compat/compat-marker.ts";
|
|
@@ -103,3 +104,87 @@ test("compatMarkerPath resolves under .gsd", () => {
|
|
|
103
104
|
const base = makeTmpBase();
|
|
104
105
|
assert.equal(compatMarkerPath(base), join(base, ".gsd", ".compat.json"));
|
|
105
106
|
});
|
|
107
|
+
|
|
108
|
+
test("pruneOrphanedProjectionEntries drops entries whose backing file is gone (#1257)", () => {
|
|
109
|
+
const base = makeTmpBase();
|
|
110
|
+
// Live projection: file exists on disk.
|
|
111
|
+
const liveRel = "phases/29-frontend-code-debt-cleanup/29-ROADMAP.md";
|
|
112
|
+
mkdirSync(join(base, ".gsd", "phases", "29-frontend-code-debt-cleanup"), { recursive: true });
|
|
113
|
+
writeFileSync(join(base, ".gsd", liveRel), "# M029\n", "utf-8");
|
|
114
|
+
// Phantom projection: directory was renamed, file no longer exists.
|
|
115
|
+
const ghostRel = "phases/29-new-milestone-m029/29-ROADMAP.md";
|
|
116
|
+
|
|
117
|
+
writeCompatMarker(base, {
|
|
118
|
+
schema: 2,
|
|
119
|
+
lastWriter: "gsd-pi",
|
|
120
|
+
lastProjectedAt: "2026-07-05T05:35:19.379Z",
|
|
121
|
+
projections: {
|
|
122
|
+
[liveRel]: { sha: "live000000000000", entities: ["M029"] },
|
|
123
|
+
[ghostRel]: { sha: "ghost00000000000", entities: ["M029"] },
|
|
124
|
+
},
|
|
125
|
+
piVersion: "1.4.0",
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
const removed = pruneOrphanedProjectionEntries(base);
|
|
129
|
+
assert.equal(removed, 1);
|
|
130
|
+
|
|
131
|
+
const marker = readCompatMarker(base);
|
|
132
|
+
assert.ok(marker.projections[liveRel], "live projection must survive");
|
|
133
|
+
assert.equal(marker.projections[ghostRel], undefined, "phantom projection must be pruned");
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test("pruneOrphanedProjectionEntries also prunes .planning projections/passthrough", () => {
|
|
137
|
+
const base = makeTmpBase();
|
|
138
|
+
mkdirSync(join(base, ".planning"), { recursive: true });
|
|
139
|
+
writeFileSync(join(base, ".planning", "ROADMAP.md"), "# roadmap\n", "utf-8");
|
|
140
|
+
|
|
141
|
+
writeCompatMarker(base, {
|
|
142
|
+
schema: 2,
|
|
143
|
+
lastWriter: "gsd-pi",
|
|
144
|
+
lastProjectedAt: "2026-07-05T05:35:19.379Z",
|
|
145
|
+
projections: {},
|
|
146
|
+
planning: {
|
|
147
|
+
active: true,
|
|
148
|
+
layout: "flat-phases",
|
|
149
|
+
projections: {
|
|
150
|
+
"ROADMAP.md": { sha: "live000000000000", entities: [] },
|
|
151
|
+
"gone/PLAN.md": { sha: "ghost00000000000", entities: [] },
|
|
152
|
+
},
|
|
153
|
+
passthrough: {
|
|
154
|
+
"gone/PATTERNS.md": { sha: "ghost11111111111", entities: [] },
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
piVersion: "1.4.0",
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const removed = pruneOrphanedProjectionEntries(base);
|
|
161
|
+
assert.equal(removed, 2);
|
|
162
|
+
|
|
163
|
+
const marker = readCompatMarker(base);
|
|
164
|
+
assert.ok(marker.planning!.projections["ROADMAP.md"], "live planning projection must survive");
|
|
165
|
+
assert.equal(marker.planning!.projections["gone/PLAN.md"], undefined);
|
|
166
|
+
assert.equal(marker.planning!.passthrough["gone/PATTERNS.md"], undefined);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test("pruneOrphanedProjectionEntries is a no-op when nothing is orphaned", () => {
|
|
170
|
+
const base = makeTmpBase();
|
|
171
|
+
const rel = "phases/01-phase/01-01-PLAN.md";
|
|
172
|
+
mkdirSync(join(base, ".gsd", "phases", "01-phase"), { recursive: true });
|
|
173
|
+
writeFileSync(join(base, ".gsd", rel), "# plan\n", "utf-8");
|
|
174
|
+
writeCompatMarker(base, {
|
|
175
|
+
schema: 2,
|
|
176
|
+
lastWriter: "gsd-pi",
|
|
177
|
+
lastProjectedAt: "2026-07-05T05:35:19.379Z",
|
|
178
|
+
projections: { [rel]: { sha: "abc000000000000", entities: ["M001"] } },
|
|
179
|
+
piVersion: "1.4.0",
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
assert.equal(pruneOrphanedProjectionEntries(base), 0);
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
test("pruneOrphanedProjectionEntries returns 0 and writes nothing when marker is missing", () => {
|
|
186
|
+
const base = makeTmpBase();
|
|
187
|
+
assert.equal(pruneOrphanedProjectionEntries(base), 0);
|
|
188
|
+
const files = readdirSync(join(base, ".gsd"));
|
|
189
|
+
assert.ok(!files.includes(".compat.json"), "must not create a marker where none existed");
|
|
190
|
+
});
|
|
@@ -422,6 +422,51 @@ console.log('\n=== complete-milestone: existing SUMMARY.md preserved (#4598) ===
|
|
|
422
422
|
cleanup(dbPath);
|
|
423
423
|
}
|
|
424
424
|
|
|
425
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
426
|
+
// complete-milestone: flat-phase compatibility SUMMARY.md is not hidden
|
|
427
|
+
// ═══════════════════════════════════════════════════════════════════════════
|
|
428
|
+
|
|
429
|
+
console.log('\n=== complete-milestone: flat-phase legacy-named SUMMARY.md preserved (#4598) ===');
|
|
430
|
+
{
|
|
431
|
+
const dbPath = tempDbPath();
|
|
432
|
+
openDatabase(dbPath);
|
|
433
|
+
const basePath = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-flat-complete-milestone-'));
|
|
434
|
+
const phaseDir = path.join(basePath, '.gsd', 'phases', '01-test-milestone');
|
|
435
|
+
fs.mkdirSync(path.join(phaseDir, 'slices', 'S01', 'tasks'), { recursive: true });
|
|
436
|
+
seedCompletedMilestone({ basePath, validationVerdict: 'pass' });
|
|
437
|
+
|
|
438
|
+
// Flat-phase projects may still contain legacy-named compatibility files.
|
|
439
|
+
// The no-overwrite guard must preserve those richer summaries instead of
|
|
440
|
+
// generating a canonical sibling that later readers would prefer.
|
|
441
|
+
const summaryPath = path.join(phaseDir, 'M001-SUMMARY.md');
|
|
442
|
+
const canonicalSummaryPath = path.join(phaseDir, '01-SUMMARY.md');
|
|
443
|
+
const sentinel = '# M001: Pre-existing flat-phase richer summary\n\nDO NOT OVERWRITE ME\n';
|
|
444
|
+
fs.writeFileSync(summaryPath, sentinel, 'utf-8');
|
|
445
|
+
|
|
446
|
+
const result = await handleCompleteMilestone(makeValidParams(), basePath);
|
|
447
|
+
assertTrue(!('error' in result), 'completion should still succeed when flat-phase compatibility SUMMARY.md exists');
|
|
448
|
+
if (!('error' in result)) {
|
|
449
|
+
assertEq(
|
|
450
|
+
result.summaryPath,
|
|
451
|
+
fs.realpathSync(summaryPath),
|
|
452
|
+
'completion should report the preserved existing SUMMARY path',
|
|
453
|
+
);
|
|
454
|
+
assertEq(
|
|
455
|
+
fs.readFileSync(summaryPath, 'utf-8'),
|
|
456
|
+
sentinel,
|
|
457
|
+
'flat-phase compatibility SUMMARY.md must be preserved, not hidden by a canonical rewrite',
|
|
458
|
+
);
|
|
459
|
+
assertTrue(
|
|
460
|
+
!fs.existsSync(canonicalSummaryPath),
|
|
461
|
+
'completion must not create a canonical sibling over an existing compatibility summary',
|
|
462
|
+
);
|
|
463
|
+
assertEq(getMilestone('M001')!.status, 'complete', 'milestone should still be marked complete in DB');
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
cleanupDir(basePath);
|
|
467
|
+
cleanup(dbPath);
|
|
468
|
+
}
|
|
469
|
+
|
|
425
470
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
426
471
|
|
|
427
472
|
report();
|
|
@@ -33,6 +33,45 @@ test("normalizeDbRows normalizes every row", () => {
|
|
|
33
33
|
assert.deepEqual(normalizeDbRows([first, second]), [{ id: "S01" }, { id: "S02" }]);
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
+
test("createDbAdapter strips named-parameter key prefixes for provider compatibility", () => {
|
|
37
|
+
const received: unknown[][] = [];
|
|
38
|
+
const rawStatement = {
|
|
39
|
+
run: (...params: unknown[]) => {
|
|
40
|
+
received.push(params);
|
|
41
|
+
return { changes: 1 };
|
|
42
|
+
},
|
|
43
|
+
get: (...params: unknown[]) => {
|
|
44
|
+
received.push(params);
|
|
45
|
+
return undefined;
|
|
46
|
+
},
|
|
47
|
+
all: (...params: unknown[]) => {
|
|
48
|
+
received.push(params);
|
|
49
|
+
return [];
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
const rawDb = {
|
|
53
|
+
exec: () => {},
|
|
54
|
+
prepare: () => rawStatement,
|
|
55
|
+
close: () => {},
|
|
56
|
+
};
|
|
57
|
+
const adapter = createDbAdapter(rawDb);
|
|
58
|
+
const stmt = adapter.prepare("INSERT INTO t (version, applied_at) VALUES (:version, :applied_at)");
|
|
59
|
+
|
|
60
|
+
stmt.run({ ":version": 1, "@name": "a", "$flag": true, bare: "kept" });
|
|
61
|
+
assert.deepEqual(received.pop(), [{ version: 1, name: "a", flag: true, bare: "kept" }]);
|
|
62
|
+
|
|
63
|
+
// Non-binding params pass through untouched: positional scalars, arrays,
|
|
64
|
+
// and binary blobs must not be treated as named-binding objects.
|
|
65
|
+
const blob = new Uint8Array([1, 2, 3]);
|
|
66
|
+
stmt.get("T01", blob);
|
|
67
|
+
const passthrough = received.pop() as unknown[];
|
|
68
|
+
assert.equal(passthrough[0], "T01");
|
|
69
|
+
assert.equal(passthrough[1], blob);
|
|
70
|
+
|
|
71
|
+
stmt.all({ plain: "unchanged" });
|
|
72
|
+
assert.deepEqual(received.pop(), [{ plain: "unchanged" }]);
|
|
73
|
+
});
|
|
74
|
+
|
|
36
75
|
test("createDbAdapter caches prepared statements and clears cache on close", () => {
|
|
37
76
|
const calls: unknown[] = [];
|
|
38
77
|
const rawStatement = {
|