@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
|
@@ -1033,6 +1033,26 @@ export function renderPreferencesForSystemPrompt(
|
|
|
1033
1033
|
return lines.join("\n");
|
|
1034
1034
|
}
|
|
1035
1035
|
|
|
1036
|
+
/**
|
|
1037
|
+
* Render a compact response-language directive for injection into dispatched
|
|
1038
|
+
* prompt *content* (discuss/QA and auto-execution unit prompts).
|
|
1039
|
+
*
|
|
1040
|
+
* The main session's system prompt already carries the full GSD Skill
|
|
1041
|
+
* Preferences block (including language) via `before_agent_start`, but
|
|
1042
|
+
* discuss/QA turns and auto-execution units are dispatched as separate
|
|
1043
|
+
* turns/sessions whose prompts are assembled here — without that block — so the
|
|
1044
|
+
* configured language never reaches them (#1210). Injecting this directive into
|
|
1045
|
+
* the dispatched prompt content makes the `language` preference effective across
|
|
1046
|
+
* both guided and auto workflows.
|
|
1047
|
+
*
|
|
1048
|
+
* Returns "" when no language is configured.
|
|
1049
|
+
*/
|
|
1050
|
+
export function renderLanguageDirectiveForPrompt(preferences: GSDPreferences | undefined): string {
|
|
1051
|
+
if (!preferences?.language) return "";
|
|
1052
|
+
const safeLang = preferences.language.replace(/[\r\n]/g, " ").slice(0, 50);
|
|
1053
|
+
return `## Response Language\n\nAlways respond in ${safeLang} — questions, explanations, and summaries.`;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1036
1056
|
// ─── Hook Resolution ──────────────────────────────────────────────────────────
|
|
1037
1057
|
|
|
1038
1058
|
/**
|
|
@@ -22,8 +22,9 @@ import type {
|
|
|
22
22
|
} from "./types.js";
|
|
23
23
|
import { resolvePostUnitHooks, resolvePreDispatchHooks } from "./preferences.js";
|
|
24
24
|
import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
|
|
25
|
-
import { join } from "node:path";
|
|
25
|
+
import { dirname, join } from "node:path";
|
|
26
26
|
import { parseUnitId } from "./unit-id.js";
|
|
27
|
+
import { resolveMilestonePath } from "./paths.js";
|
|
27
28
|
import { queryJournal, type JournalEntry } from "./journal.js";
|
|
28
29
|
import { readUnitRuntimeRecord, type UnitRuntimePhase } from "./unit-runtime.js";
|
|
29
30
|
import { extractFrontmatterVerdict } from "./verdict-parser.js";
|
|
@@ -32,13 +33,45 @@ import { extractFrontmatterVerdict } from "./verdict-parser.js";
|
|
|
32
33
|
|
|
33
34
|
export function resolveHookArtifactPath(basePath: string, unitId: string, artifactName: string): string {
|
|
34
35
|
const { milestone, slice, task } = parseUnitId(unitId);
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
|
|
37
|
+
// Prefer the active phase directory (flat-phase layout: .gsd/phases/<NN>-.../).
|
|
38
|
+
// Configured gate artifacts are canonical phase-level files (e.g.
|
|
39
|
+
// BROWSER-RUNTIME-EVIDENCE.md), so resolve the exact declared name first,
|
|
40
|
+
// then a task-prefixed variant for retry sentinels / rework briefs.
|
|
41
|
+
const candidates: string[] = [];
|
|
42
|
+
const phaseDir = resolveMilestonePath(basePath, milestone);
|
|
43
|
+
const isFlatPhaseDir = phaseDir !== null
|
|
44
|
+
&& !(dirname(phaseDir).endsWith("/milestones") || dirname(phaseDir).endsWith("\\milestones"));
|
|
45
|
+
if (isFlatPhaseDir) {
|
|
46
|
+
candidates.push(join(phaseDir, artifactName));
|
|
47
|
+
if (task !== undefined) {
|
|
48
|
+
candidates.push(join(phaseDir, `${task}-${artifactName}`));
|
|
49
|
+
}
|
|
37
50
|
}
|
|
38
|
-
|
|
39
|
-
|
|
51
|
+
|
|
52
|
+
// Legacy nested layout fallbacks (.gsd/milestones/<M>/slices/<S>/tasks/...).
|
|
53
|
+
const legacyBase = join(basePath, ".gsd", "milestones", milestone);
|
|
54
|
+
let legacyDefault: string;
|
|
55
|
+
if (task !== undefined && slice !== undefined) {
|
|
56
|
+
legacyDefault = join(legacyBase, "slices", slice, "tasks", `${task}-${artifactName}`);
|
|
57
|
+
candidates.push(legacyDefault);
|
|
58
|
+
candidates.push(join(legacyBase, "slices", slice, "tasks", artifactName));
|
|
59
|
+
candidates.push(join(legacyBase, "slices", slice, artifactName));
|
|
60
|
+
} else if (slice !== undefined) {
|
|
61
|
+
legacyDefault = join(legacyBase, "slices", slice, artifactName);
|
|
62
|
+
candidates.push(legacyDefault);
|
|
63
|
+
} else {
|
|
64
|
+
legacyDefault = join(legacyBase, artifactName);
|
|
40
65
|
}
|
|
41
|
-
|
|
66
|
+
candidates.push(join(legacyBase, artifactName));
|
|
67
|
+
|
|
68
|
+
const existing = candidates.find((candidate) => existsSync(candidate));
|
|
69
|
+
if (existing) return existing;
|
|
70
|
+
|
|
71
|
+
// Nothing on disk yet: prefer the active phase path when the phase dir is
|
|
72
|
+
// known, otherwise fall back to the legacy default (preserves pre-flat-phase
|
|
73
|
+
// behavior for missing-artifact diagnostics).
|
|
74
|
+
return isFlatPhaseDir ? join(phaseDir!, artifactName) : legacyDefault;
|
|
42
75
|
}
|
|
43
76
|
|
|
44
77
|
// ─── Dispatch Rule Conversion ──────────────────────────────────────────────
|
|
@@ -126,6 +159,13 @@ export class RuleRegistry {
|
|
|
126
159
|
forceRun?: boolean;
|
|
127
160
|
}> = [];
|
|
128
161
|
cycleCounts: Map<string, number> = new Map();
|
|
162
|
+
/**
|
|
163
|
+
* Cycle keys that have already been granted a one-shot re-dispatch after a
|
|
164
|
+
* lost/interrupted dispatch (hook charged a cycle but never produced its own
|
|
165
|
+
* unit-end). Bounds the dispatch-cycle refund to exactly one per gate so a
|
|
166
|
+
* hook that repeatedly fails to complete cannot loop forever.
|
|
167
|
+
*/
|
|
168
|
+
redispatchedGateKeys: Set<string> = new Set();
|
|
129
169
|
retryPending: boolean = false;
|
|
130
170
|
retryTrigger: { unitType: string; unitId: string; retryArtifact?: string } | null = null;
|
|
131
171
|
hookFailure: HookFailureState | null = null;
|
|
@@ -364,6 +404,14 @@ export class RuleRegistry {
|
|
|
364
404
|
pendingRetry: false,
|
|
365
405
|
};
|
|
366
406
|
|
|
407
|
+
return this._buildHookDispatch(config, triggerUnitId);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
/** Construct the sidecar dispatch for a hook without mutating registry state. */
|
|
411
|
+
private _buildHookDispatch(
|
|
412
|
+
config: PostUnitHookConfig,
|
|
413
|
+
triggerUnitId: string,
|
|
414
|
+
): HookDispatchResult {
|
|
367
415
|
const { milestone: mid, slice: sid, task: tid } = parseUnitId(triggerUnitId);
|
|
368
416
|
let prompt = config.prompt
|
|
369
417
|
.replace(/\{milestoneId\}/g, mid ?? "")
|
|
@@ -375,12 +423,29 @@ export class RuleRegistry {
|
|
|
375
423
|
return {
|
|
376
424
|
hookName: config.name,
|
|
377
425
|
prompt,
|
|
378
|
-
|
|
426
|
+
// Model selection (including fallbacks[]) is handled by
|
|
427
|
+
// resolveModelWithFallbacksForUnit for the `hook/<name>` unit type (#1229).
|
|
379
428
|
unitType: `hook/${config.name}`,
|
|
380
429
|
unitId: triggerUnitId,
|
|
381
430
|
};
|
|
382
431
|
}
|
|
383
432
|
|
|
433
|
+
/**
|
|
434
|
+
* Reconstruct the in-flight hook's dispatch from the restored `activeHook`
|
|
435
|
+
* state, without mutating the registry. Used to reconcile a persisted
|
|
436
|
+
* `activeHook` whose session-local dispatch was lost across a pause/resume
|
|
437
|
+
* or crash-recovery, so the hook actually runs instead of being charged
|
|
438
|
+
* against the next unrelated unit's close-out (#1246).
|
|
439
|
+
*/
|
|
440
|
+
getPendingHookDispatch(basePath: string): HookDispatchResult | null {
|
|
441
|
+
if (!this.activeHook) return null;
|
|
442
|
+
const config = resolvePostUnitHooks(basePath).find(
|
|
443
|
+
h => h.name === this.activeHook!.hookName,
|
|
444
|
+
);
|
|
445
|
+
if (!config) return null;
|
|
446
|
+
return this._buildHookDispatch(config, this.activeHook.triggerUnitId);
|
|
447
|
+
}
|
|
448
|
+
|
|
384
449
|
private _assessHookCompletion(
|
|
385
450
|
basePath: string,
|
|
386
451
|
hook: HookExecutionState,
|
|
@@ -503,6 +568,30 @@ export class RuleRegistry {
|
|
|
503
568
|
observedCleanExecution: boolean,
|
|
504
569
|
): HookDispatchResult | null {
|
|
505
570
|
if (!observedCleanExecution) {
|
|
571
|
+
// The hook was charged a cycle at dispatch but never produced its own
|
|
572
|
+
// unit-end — its dispatch was lost/interrupted (pause/resume, crash
|
|
573
|
+
// recovery). Refund that dispatch-consumed cycle exactly once so the gate
|
|
574
|
+
// is re-dispatched at least once instead of hard-blocking on a hook that
|
|
575
|
+
// never actually ran.
|
|
576
|
+
//
|
|
577
|
+
// This refund is intentionally UNCONDITIONAL on the first lost dispatch —
|
|
578
|
+
// it must NOT be gated on `currentCycle >= max_cycles`. A lost dispatch is
|
|
579
|
+
// a non-run: charging its cycle at dispatch time must not consume the
|
|
580
|
+
// hook's real-run budget. Gating the refund on "budget already exhausted"
|
|
581
|
+
// lets a lost dispatch below max_cycles silently eat a genuine cycle, so
|
|
582
|
+
// the hook gets one fewer real run than configured — reintroducing #1244
|
|
583
|
+
// for max_cycles >= 2 (see the "does not steal a real-run cycle" test).
|
|
584
|
+
// The redispatchedGateKeys flag bounds this to a single refund per gate,
|
|
585
|
+
// so repeated lost dispatches still terminate (total dispatches never
|
|
586
|
+
// exceed max_cycles + 1) and a hook that keeps failing blocks normally.
|
|
587
|
+
const cycleKey = hookCycleKey(config, hook);
|
|
588
|
+
if (!this.redispatchedGateKeys.has(cycleKey)) {
|
|
589
|
+
this.redispatchedGateKeys.add(cycleKey);
|
|
590
|
+
const currentCycle = this.cycleCounts.get(cycleKey);
|
|
591
|
+
if (typeof currentCycle === "number" && currentCycle > 0) {
|
|
592
|
+
this.cycleCounts.set(cycleKey, currentCycle - 1);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
506
595
|
return this._rerunGateOrBlock(config, hook, basePath, {
|
|
507
596
|
reason: `hook/${config.name} did not complete cleanly before the trigger unit resumed`,
|
|
508
597
|
});
|
|
@@ -839,6 +928,7 @@ export class RuleRegistry {
|
|
|
839
928
|
this.activeHook = null;
|
|
840
929
|
this.hookQueue = [];
|
|
841
930
|
this.cycleCounts.clear();
|
|
931
|
+
this.redispatchedGateKeys.clear();
|
|
842
932
|
this.retryPending = false;
|
|
843
933
|
this.retryTrigger = null;
|
|
844
934
|
this.hookFailure = null;
|
|
@@ -855,6 +945,7 @@ export class RuleRegistry {
|
|
|
855
945
|
persistState(basePath: string): void {
|
|
856
946
|
const state: PersistedHookState = {
|
|
857
947
|
cycleCounts: Object.fromEntries(this.cycleCounts),
|
|
948
|
+
redispatchedGateKeys: Array.from(this.redispatchedGateKeys),
|
|
858
949
|
activeHook: this.activeHook ? { ...this.activeHook } : null,
|
|
859
950
|
hookQueue: this.hookQueue.map(entry => ({
|
|
860
951
|
hookName: entry.config.name,
|
|
@@ -888,6 +979,14 @@ export class RuleRegistry {
|
|
|
888
979
|
}
|
|
889
980
|
}
|
|
890
981
|
}
|
|
982
|
+
this.redispatchedGateKeys.clear();
|
|
983
|
+
if (Array.isArray(state.redispatchedGateKeys)) {
|
|
984
|
+
for (const key of state.redispatchedGateKeys) {
|
|
985
|
+
if (typeof key === "string") {
|
|
986
|
+
this.redispatchedGateKeys.add(key);
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
}
|
|
891
990
|
this.activeHook = state.activeHook && typeof state.activeHook === "object"
|
|
892
991
|
? { ...state.activeHook }
|
|
893
992
|
: null;
|
|
@@ -918,7 +1017,7 @@ export class RuleRegistry {
|
|
|
918
1017
|
if (existsSync(filePath)) {
|
|
919
1018
|
writeFileSync(
|
|
920
1019
|
filePath,
|
|
921
|
-
JSON.stringify({ cycleCounts: {}, activeHook: null, hookQueue: [], savedAt: new Date().toISOString() }, null, 2),
|
|
1020
|
+
JSON.stringify({ cycleCounts: {}, redispatchedGateKeys: [], activeHook: null, hookQueue: [], savedAt: new Date().toISOString() }, null, 2),
|
|
922
1021
|
"utf-8",
|
|
923
1022
|
);
|
|
924
1023
|
}
|
|
@@ -1014,7 +1113,10 @@ export class RuleRegistry {
|
|
|
1014
1113
|
return {
|
|
1015
1114
|
hookName: hook.name,
|
|
1016
1115
|
prompt,
|
|
1017
|
-
|
|
1116
|
+
// Model selection (including fallbacks[]) is resolved by dispatchHookUnit
|
|
1117
|
+
// via resolveModelWithFallbacksForUnit for the `hook/<name>` unit type,
|
|
1118
|
+
// matching the auto-mode path. Emitting the primary-only model here would
|
|
1119
|
+
// discard the configured fallback chain (#1229).
|
|
1018
1120
|
unitType: `hook/${hook.name}`,
|
|
1019
1121
|
unitId,
|
|
1020
1122
|
};
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { existsSync, readdirSync, readFileSync, type Dirent } from "node:fs";
|
|
12
|
-
import { homedir } from "node:os";
|
|
13
12
|
import { join } from "node:path";
|
|
13
|
+
import { getSkillDirectories } from "@gsd/pi-coding-agent";
|
|
14
14
|
import { gsdHome } from "./gsd-home.js";
|
|
15
15
|
import { getInstalledSkills, normalizeSkillName } from "./skills.js";
|
|
16
16
|
|
|
@@ -20,28 +20,47 @@ export interface DiscoveredSkill {
|
|
|
20
20
|
location: string;
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
/**
|
|
24
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Snapshot state. `searchDirs` is captured at snapshot time so every detection
|
|
25
|
+
* pass during the session scans the same project + user dirs (the bug fix:
|
|
26
|
+
* previously only user dirs were scanned, so skills installed into the project
|
|
27
|
+
* mid-session were silently missed).
|
|
28
|
+
*/
|
|
29
|
+
interface SkillSnapshotState {
|
|
30
|
+
baselineNames: Set<string>;
|
|
31
|
+
searchDirs: string[];
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
let snapshot: SkillSnapshotState | null = null;
|
|
25
35
|
|
|
26
36
|
/**
|
|
27
37
|
* Snapshot the current installed skill catalog. Call at auto-mode start.
|
|
38
|
+
* `cwd` is the project base path; skills dropped into `<cwd>/.agents/skills/`,
|
|
39
|
+
* `<cwd>/.claude/skills/`, or `<cwd>/.gsd/skills/` mid-session are detected.
|
|
28
40
|
*/
|
|
29
|
-
export function snapshotSkills(): void {
|
|
30
|
-
|
|
41
|
+
export function snapshotSkills(options?: { cwd?: string }): void {
|
|
42
|
+
const searchDirs = getSkillDirectories({
|
|
43
|
+
cwd: options?.cwd ?? process.cwd(),
|
|
44
|
+
gsdHome: gsdHome(),
|
|
45
|
+
}).map((entry) => entry.path);
|
|
46
|
+
snapshot = {
|
|
47
|
+
baselineNames: snapshotCurrentSkillNames(searchDirs),
|
|
48
|
+
searchDirs,
|
|
49
|
+
};
|
|
31
50
|
}
|
|
32
51
|
|
|
33
52
|
/**
|
|
34
53
|
* Clear the snapshot. Call when auto-mode stops.
|
|
35
54
|
*/
|
|
36
55
|
export function clearSkillSnapshot(): void {
|
|
37
|
-
|
|
56
|
+
snapshot = null;
|
|
38
57
|
}
|
|
39
58
|
|
|
40
59
|
/**
|
|
41
60
|
* Check if a snapshot is active (auto-mode is running with discovery).
|
|
42
61
|
*/
|
|
43
62
|
export function hasSkillSnapshot(): boolean {
|
|
44
|
-
return
|
|
63
|
+
return snapshot !== null;
|
|
45
64
|
}
|
|
46
65
|
|
|
47
66
|
/**
|
|
@@ -49,12 +68,12 @@ export function hasSkillSnapshot(): boolean {
|
|
|
49
68
|
* Returns skill metadata for any new skills found in the loader catalog or on disk.
|
|
50
69
|
*/
|
|
51
70
|
export function detectNewSkills(): DiscoveredSkill[] {
|
|
52
|
-
if (!
|
|
71
|
+
if (!snapshot) return [];
|
|
53
72
|
|
|
54
73
|
const newSkills: DiscoveredSkill[] = [];
|
|
55
|
-
for (const skill of getCurrentSkillsForDiscovery()) {
|
|
74
|
+
for (const skill of getCurrentSkillsForDiscovery(snapshot.searchDirs)) {
|
|
56
75
|
const normalized = normalizeSkillName(skill.name);
|
|
57
|
-
if (
|
|
76
|
+
if (snapshot.baselineNames.has(normalized)) continue;
|
|
58
77
|
newSkills.push(skill);
|
|
59
78
|
}
|
|
60
79
|
|
|
@@ -84,7 +103,13 @@ export async function refreshCatalogForNewSkills(options?: {
|
|
|
84
103
|
}
|
|
85
104
|
}
|
|
86
105
|
|
|
87
|
-
|
|
106
|
+
// Re-snapshot, preserving the search dirs captured at auto-mode start.
|
|
107
|
+
if (snapshot) {
|
|
108
|
+
snapshot = {
|
|
109
|
+
baselineNames: snapshotCurrentSkillNames(),
|
|
110
|
+
searchDirs: snapshot.searchDirs,
|
|
111
|
+
};
|
|
112
|
+
}
|
|
88
113
|
const names = newSkills.map((skill) => skill.name).join(", ");
|
|
89
114
|
options?.notify?.(`GSD: loaded new skills: ${names}`, "info");
|
|
90
115
|
return newSkills;
|
|
@@ -132,19 +157,18 @@ function escapeXml(value: string): string {
|
|
|
132
157
|
});
|
|
133
158
|
}
|
|
134
159
|
|
|
135
|
-
function
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
];
|
|
160
|
+
function snapshotCurrentSkillNames(searchDirsOverride?: string[]): Set<string> {
|
|
161
|
+
// Uses explicit dirs, the snapshot's search dirs when active, or user-only dirs
|
|
162
|
+
// (no project context) when called before a snapshot is taken.
|
|
163
|
+
const searchDirs = searchDirsOverride ?? snapshot?.searchDirs ?? defaultSearchDirs();
|
|
164
|
+
return new Set(getCurrentSkillsForDiscovery(searchDirs).map(skill => normalizeSkillName(skill.name)));
|
|
141
165
|
}
|
|
142
166
|
|
|
143
|
-
function
|
|
144
|
-
return
|
|
167
|
+
function defaultSearchDirs(): string[] {
|
|
168
|
+
return getSkillDirectories({ cwd: process.cwd(), gsdHome: gsdHome() }).map((entry) => entry.path);
|
|
145
169
|
}
|
|
146
170
|
|
|
147
|
-
function getCurrentSkillsForDiscovery(): DiscoveredSkill[] {
|
|
171
|
+
function getCurrentSkillsForDiscovery(searchDirs: string[]): DiscoveredSkill[] {
|
|
148
172
|
const skills = new Map<string, DiscoveredSkill>();
|
|
149
173
|
for (const skill of getInstalledSkills()) {
|
|
150
174
|
const normalized = normalizeSkillName(skill.name);
|
|
@@ -155,7 +179,7 @@ function getCurrentSkillsForDiscovery(): DiscoveredSkill[] {
|
|
|
155
179
|
});
|
|
156
180
|
}
|
|
157
181
|
|
|
158
|
-
for (const skill of getDiskSkills()) {
|
|
182
|
+
for (const skill of getDiskSkills(searchDirs)) {
|
|
159
183
|
const normalized = normalizeSkillName(skill.name);
|
|
160
184
|
if (!skills.has(normalized)) skills.set(normalized, skill);
|
|
161
185
|
}
|
|
@@ -163,9 +187,9 @@ function getCurrentSkillsForDiscovery(): DiscoveredSkill[] {
|
|
|
163
187
|
return [...skills.values()];
|
|
164
188
|
}
|
|
165
189
|
|
|
166
|
-
function getDiskSkills(): DiscoveredSkill[] {
|
|
190
|
+
function getDiskSkills(searchDirs: string[]): DiscoveredSkill[] {
|
|
167
191
|
const skills = new Map<string, DiscoveredSkill>();
|
|
168
|
-
for (const dir of
|
|
192
|
+
for (const dir of searchDirs) {
|
|
169
193
|
if (!existsSync(dir)) continue;
|
|
170
194
|
let entries: Dirent[];
|
|
171
195
|
try {
|
|
@@ -213,21 +213,13 @@ function detectArtifactDbStatusDriftForMilestone(
|
|
|
213
213
|
|
|
214
214
|
for (const task of tasks) {
|
|
215
215
|
if (isClosedStatus(task.status)) continue;
|
|
216
|
-
|
|
217
|
-
let diskTaskSummary = resolveTaskFile(
|
|
216
|
+
const diskTaskSummary = resolveTaskFile(
|
|
218
217
|
basePath,
|
|
219
218
|
milestoneId,
|
|
220
219
|
slice.id,
|
|
221
220
|
task.id,
|
|
222
221
|
"SUMMARY",
|
|
223
222
|
);
|
|
224
|
-
if (!diskTaskSummary) {
|
|
225
|
-
const phaseDir = resolveMilestonePath(basePath, milestoneId);
|
|
226
|
-
if (phaseDir) {
|
|
227
|
-
const flatSummary = join(phaseDir, `${task.id}-SUMMARY.md`);
|
|
228
|
-
if (existsSync(flatSummary)) diskTaskSummary = flatSummary;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
223
|
if (!diskTaskSummary || !existsSync(diskTaskSummary)) continue;
|
|
232
224
|
addUniqueDrift(drifts, seen, {
|
|
233
225
|
kind: "artifact-db-status-divergence",
|
|
@@ -20,11 +20,9 @@ import {
|
|
|
20
20
|
} from "../../gsd-db.js";
|
|
21
21
|
import {
|
|
22
22
|
resolveMilestoneFile,
|
|
23
|
-
resolveMilestonePath,
|
|
24
23
|
resolveSliceFile,
|
|
25
24
|
resolveTaskFile,
|
|
26
25
|
} from "../../paths.js";
|
|
27
|
-
import { join } from "node:path";
|
|
28
26
|
import type { GSDState } from "../../types.js";
|
|
29
27
|
import type { DriftContext, DriftHandler, DriftRecord } from "../types.js";
|
|
30
28
|
|
|
@@ -105,23 +103,13 @@ function collectMilestoneCompletionDrift(
|
|
|
105
103
|
for (const task of getSliceTasks(mid, slice.id)) {
|
|
106
104
|
if (!COMPLETE_STATUSES.has(task.status)) continue;
|
|
107
105
|
if (task.completed_at !== null) continue;
|
|
108
|
-
|
|
109
|
-
// Legacy: resolveTaskFile returns the tasks/TID/TID-SUMMARY.md path.
|
|
110
|
-
let taskSummary = resolveTaskFile(
|
|
106
|
+
const taskSummary = resolveTaskFile(
|
|
111
107
|
ctx.basePath,
|
|
112
108
|
mid,
|
|
113
109
|
slice.id,
|
|
114
110
|
task.id,
|
|
115
111
|
"SUMMARY",
|
|
116
112
|
);
|
|
117
|
-
// Flat-phase fallback: check the phase dir directly
|
|
118
|
-
if (!taskSummary) {
|
|
119
|
-
const phaseDir = resolveMilestonePath(ctx.basePath, mid);
|
|
120
|
-
if (phaseDir) {
|
|
121
|
-
const flatSummary = join(phaseDir, `${task.id}-SUMMARY.md`);
|
|
122
|
-
if (existsSync(flatSummary)) taskSummary = flatSummary;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
113
|
if (taskSummary && existsSync(taskSummary)) {
|
|
126
114
|
drifts.push({
|
|
127
115
|
kind: "missing-completion-timestamp",
|
|
@@ -182,15 +170,7 @@ export function repairMissingCompletionTimestamp(
|
|
|
182
170
|
task.completed_at !== null ||
|
|
183
171
|
!COMPLETE_STATUSES.has(task.status)
|
|
184
172
|
) return;
|
|
185
|
-
|
|
186
|
-
let summary = resolveTaskFile(ctx.basePath, mid, sid, tid, "SUMMARY");
|
|
187
|
-
if (!summary) {
|
|
188
|
-
const phaseDir = resolveMilestonePath(ctx.basePath, mid);
|
|
189
|
-
if (phaseDir) {
|
|
190
|
-
const flatSummary = join(phaseDir, `${tid}-SUMMARY.md`);
|
|
191
|
-
if (existsSync(flatSummary)) summary = flatSummary;
|
|
192
|
-
}
|
|
193
|
-
}
|
|
173
|
+
const summary = resolveTaskFile(ctx.basePath, mid, sid, tid, "SUMMARY");
|
|
194
174
|
const ts = summary ? summaryMtimeIso(summary) : null;
|
|
195
175
|
if (!ts) return;
|
|
196
176
|
updateTaskStatus(mid, sid, tid, task.status, ts);
|
|
@@ -73,6 +73,20 @@ export async function reconcileBeforeDispatch(
|
|
|
73
73
|
if (!deps.dryRun) {
|
|
74
74
|
const { capturePlanningCompatIfNeeded } = await import("../compat/planning-compat.js");
|
|
75
75
|
await capturePlanningCompatIfNeeded(basePath);
|
|
76
|
+
|
|
77
|
+
// Self-heal phantom projection entries (#1257): a renamed/removed phase
|
|
78
|
+
// directory leaves stale `.compat.json` projection paths that no detector
|
|
79
|
+
// ever prunes (they all skip missing files), so the marker drifts from disk
|
|
80
|
+
// reality permanently. Drop entries whose backing file is gone before the
|
|
81
|
+
// detect passes run. Gated on !dryRun to keep detection read-only.
|
|
82
|
+
const { pruneOrphanedProjectionEntries } = await import("../compat/compat-marker.js");
|
|
83
|
+
const prunedCount = pruneOrphanedProjectionEntries(basePath);
|
|
84
|
+
if (prunedCount > 0) {
|
|
85
|
+
logWarning(
|
|
86
|
+
"reconcile",
|
|
87
|
+
`pruned ${prunedCount} orphaned projection entr${prunedCount === 1 ? "y" : "ies"} from .compat.json (backing file no longer on disk)`,
|
|
88
|
+
);
|
|
89
|
+
}
|
|
76
90
|
}
|
|
77
91
|
|
|
78
92
|
for (let pass = 0; pass < MAX_PASSES; pass++) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import test from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
|
-
import { mkdtempSync, mkdirSync, realpathSync, rmSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { mkdtempSync, mkdirSync, realpathSync, rmSync, utimesSync, writeFileSync } from "node:fs";
|
|
4
4
|
import { join } from "node:path";
|
|
5
5
|
import { tmpdir } from "node:os";
|
|
6
6
|
|
|
@@ -360,6 +360,174 @@ test("canonical worktree + project-root legacy dir produces legacy filename (#bu
|
|
|
360
360
|
}
|
|
361
361
|
});
|
|
362
362
|
|
|
363
|
+
// ── cursor[bot] 3523932401: bare milestone id must not match a team-suffix ────
|
|
364
|
+
//
|
|
365
|
+
// A bare milestone id (M001, no team-mode suffix) resolves its phase dir by
|
|
366
|
+
// scanning phases/NN-*. Before the guard (commit 8f45bf3),
|
|
367
|
+
// phaseDirMatchesMilestoneId returned true for ANY same-number dir, so a
|
|
368
|
+
// leftover team-mode projection (phases/01-<6char>-.../, from a prior
|
|
369
|
+
// team-suffixed run) matched the bare id too. When that stale dir carried a
|
|
370
|
+
// newer mtime, pickPreferredPhaseDir selected it over the real title dir and
|
|
371
|
+
// resolved the ROADMAP into a directory that has no ROADMAP — the file-not-found
|
|
372
|
+
// retry loop. The guard (slugLooksLikeTeamSuffixProjection) rejects
|
|
373
|
+
// team-suffix-looking slugs for bare ids in the primary pass; the suffixed
|
|
374
|
+
// projection stays available only as a fallback when the primary pass finds
|
|
375
|
+
// nothing (#1195, guarded by the second test below).
|
|
376
|
+
|
|
377
|
+
test("bare milestone id prefers the real title dir over a newer team-suffix projection (cursor 3523932401)", () => {
|
|
378
|
+
const root = realpathSync(mkdtempSync(join(tmpdir(), "gsd-bare-teamsuffix-")));
|
|
379
|
+
try {
|
|
380
|
+
const phasesDir = join(root, ".gsd", "phases");
|
|
381
|
+
// Intended flat-phase dir for the bare id: 01-<title-slug>/ with the ROADMAP.
|
|
382
|
+
const intendedDir = join(phasesDir, "01-clean-continuation");
|
|
383
|
+
mkdirSync(intendedDir, { recursive: true });
|
|
384
|
+
writeFileSync(join(intendedDir, "01-ROADMAP.md"), "# roadmap\n");
|
|
385
|
+
|
|
386
|
+
// Leftover team-mode projection: 01-<6char>-.../ with NO ROADMAP, made
|
|
387
|
+
// NEWER so an mtime tiebreak would wrongly prefer it without the guard.
|
|
388
|
+
const staleDir = join(phasesDir, "01-re4q3k-old-team-projection");
|
|
389
|
+
mkdirSync(staleDir, { recursive: true });
|
|
390
|
+
const newer = new Date(Date.now() + 60_000);
|
|
391
|
+
const older = new Date(Date.now() - 60_000);
|
|
392
|
+
utimesSync(staleDir, newer, newer);
|
|
393
|
+
utimesSync(intendedDir, older, older);
|
|
394
|
+
|
|
395
|
+
_clearGsdRootCache();
|
|
396
|
+
clearPathCache();
|
|
397
|
+
|
|
398
|
+
const resolved = resolveExpectedArtifactPath("plan-milestone", "M001", root);
|
|
399
|
+
assert.equal(
|
|
400
|
+
resolved,
|
|
401
|
+
join(intendedDir, "01-ROADMAP.md"),
|
|
402
|
+
"bare id must resolve to the real title dir, not the newer team-suffix projection",
|
|
403
|
+
);
|
|
404
|
+
assert.notEqual(
|
|
405
|
+
resolved,
|
|
406
|
+
join(staleDir, "01-ROADMAP.md"),
|
|
407
|
+
"must NOT resolve into the team-suffix projection dir that has no ROADMAP",
|
|
408
|
+
);
|
|
409
|
+
} finally {
|
|
410
|
+
_clearGsdRootCache();
|
|
411
|
+
clearPathCache();
|
|
412
|
+
rmSync(root, { recursive: true, force: true });
|
|
413
|
+
}
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
test("bare milestone id still falls back to a lone team-suffix projection (#1195)", () => {
|
|
417
|
+
const root = realpathSync(mkdtempSync(join(tmpdir(), "gsd-bare-teamsuffix-only-")));
|
|
418
|
+
try {
|
|
419
|
+
const phasesDir = join(root, ".gsd", "phases");
|
|
420
|
+
// The only on-disk dir for the bare id is a team-mode projection. With no
|
|
421
|
+
// non-suffixed candidate, the fallback pass must still resolve to it.
|
|
422
|
+
const onlyDir = join(phasesDir, "01-re4q3k-clean-continuation");
|
|
423
|
+
mkdirSync(onlyDir, { recursive: true });
|
|
424
|
+
writeFileSync(join(onlyDir, "01-ROADMAP.md"), "# roadmap\n");
|
|
425
|
+
|
|
426
|
+
_clearGsdRootCache();
|
|
427
|
+
clearPathCache();
|
|
428
|
+
|
|
429
|
+
const resolved = resolveExpectedArtifactPath("plan-milestone", "M001", root);
|
|
430
|
+
assert.equal(
|
|
431
|
+
resolved,
|
|
432
|
+
join(onlyDir, "01-ROADMAP.md"),
|
|
433
|
+
"with no non-suffixed dir, the team-suffix projection is the correct fallback",
|
|
434
|
+
);
|
|
435
|
+
} finally {
|
|
436
|
+
_clearGsdRootCache();
|
|
437
|
+
clearPathCache();
|
|
438
|
+
rmSync(root, { recursive: true, force: true });
|
|
439
|
+
}
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
// ── #1208: flat-phase execute-task summaries resolve at the phase root ────────
|
|
443
|
+
//
|
|
444
|
+
// In flat-phase layout task summaries are written beside the plan files
|
|
445
|
+
// (phases/<phase>/T##-SUMMARY.md), NOT under a tasks/ subdir. A tasks/ dir may
|
|
446
|
+
// still exist to hold auxiliary task-scoped gate artifacts (e.g. T01-VERIFY.json).
|
|
447
|
+
// Before the fix, the mere existence of that tasks/ dir redirected summary
|
|
448
|
+
// verification into tasks/T##-SUMMARY.md — which never exists — trapping auto-mode
|
|
449
|
+
// in a false verification retry that eventually paused after a successful task.
|
|
450
|
+
|
|
451
|
+
test("flat-phase execute-task summary resolves at phase root when no tasks/ dir exists (#1208)", () => {
|
|
452
|
+
const root = realpathSync(mkdtempSync(join(tmpdir(), "gsd-flat-task-summary-")));
|
|
453
|
+
try {
|
|
454
|
+
const gsd = join(root, ".gsd");
|
|
455
|
+
const phaseDir = join(gsd, "phases", "49-end-to-end-mandates");
|
|
456
|
+
mkdirSync(phaseDir, { recursive: true });
|
|
457
|
+
writeFileSync(join(phaseDir, "49-03-PLAN.md"), "# plan\n- [x] T02\n");
|
|
458
|
+
|
|
459
|
+
_clearGsdRootCache();
|
|
460
|
+
clearPathCache();
|
|
461
|
+
|
|
462
|
+
assert.equal(
|
|
463
|
+
resolveExpectedArtifactPath("execute-task", "M049/S03/T02", root),
|
|
464
|
+
join(phaseDir, "T02-SUMMARY.md"),
|
|
465
|
+
);
|
|
466
|
+
} finally {
|
|
467
|
+
_clearGsdRootCache();
|
|
468
|
+
clearPathCache();
|
|
469
|
+
rmSync(root, { recursive: true, force: true });
|
|
470
|
+
}
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
test("flat-phase execute-task summary resolves at phase root even when tasks/ dir holds gate artifacts (#1208)", () => {
|
|
474
|
+
const root = realpathSync(mkdtempSync(join(tmpdir(), "gsd-flat-task-tasksdir-")));
|
|
475
|
+
try {
|
|
476
|
+
const gsd = join(root, ".gsd");
|
|
477
|
+
const phaseDir = join(gsd, "phases", "49-end-to-end-mandates");
|
|
478
|
+
mkdirSync(phaseDir, { recursive: true });
|
|
479
|
+
writeFileSync(join(phaseDir, "49-03-PLAN.md"), "# plan\n- [x] T02\n");
|
|
480
|
+
// Auxiliary task-scoped gate artifact — creates the tasks/ dir but must not
|
|
481
|
+
// redirect the T02 summary into tasks/.
|
|
482
|
+
const tasksDir = join(phaseDir, "tasks");
|
|
483
|
+
mkdirSync(tasksDir, { recursive: true });
|
|
484
|
+
writeFileSync(join(tasksDir, "T01-VERIFY.json"), '{"ok":true}');
|
|
485
|
+
|
|
486
|
+
_clearGsdRootCache();
|
|
487
|
+
clearPathCache();
|
|
488
|
+
|
|
489
|
+
const resolved = resolveExpectedArtifactPath("execute-task", "M049/S03/T02", root);
|
|
490
|
+
assert.equal(
|
|
491
|
+
resolved,
|
|
492
|
+
join(phaseDir, "T02-SUMMARY.md"),
|
|
493
|
+
"flat-phase task summary must resolve at the phase root, not tasks/",
|
|
494
|
+
);
|
|
495
|
+
assert.notEqual(
|
|
496
|
+
resolved,
|
|
497
|
+
join(tasksDir, "T02-SUMMARY.md"),
|
|
498
|
+
"a tasks/ dir with gate artifacts must NOT redirect summary resolution into tasks/",
|
|
499
|
+
);
|
|
500
|
+
} finally {
|
|
501
|
+
_clearGsdRootCache();
|
|
502
|
+
clearPathCache();
|
|
503
|
+
rmSync(root, { recursive: true, force: true });
|
|
504
|
+
}
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
test("legacy execute-task summary still resolves under slices/<SID>/tasks/ (#1208 regression guard)", () => {
|
|
508
|
+
const root = realpathSync(mkdtempSync(join(tmpdir(), "gsd-legacy-task-summary-")));
|
|
509
|
+
try {
|
|
510
|
+
const milestoneDir = join(root, ".gsd", "milestones", "M001");
|
|
511
|
+
const sliceDir = join(milestoneDir, "slices", "S01");
|
|
512
|
+
const tasksDir = join(sliceDir, "tasks");
|
|
513
|
+
mkdirSync(tasksDir, { recursive: true });
|
|
514
|
+
writeFileSync(join(milestoneDir, "M001-CONTEXT.md"), "# context\n");
|
|
515
|
+
writeFileSync(join(sliceDir, "S01-PLAN.md"), "# plan\n");
|
|
516
|
+
|
|
517
|
+
_clearGsdRootCache();
|
|
518
|
+
clearPathCache();
|
|
519
|
+
|
|
520
|
+
assert.equal(
|
|
521
|
+
resolveExpectedArtifactPath("execute-task", "M001/S01/T02", root),
|
|
522
|
+
join(tasksDir, "T02-SUMMARY.md"),
|
|
523
|
+
);
|
|
524
|
+
} finally {
|
|
525
|
+
_clearGsdRootCache();
|
|
526
|
+
clearPathCache();
|
|
527
|
+
rmSync(root, { recursive: true, force: true });
|
|
528
|
+
}
|
|
529
|
+
});
|
|
530
|
+
|
|
363
531
|
// ── resolveSliceResearchLocation / resolveExistingSliceResearchPath ───────────
|
|
364
532
|
//
|
|
365
533
|
// Added in the code-quality consolidation PR as the shared dual-path resolver
|