gsd-pi 2.78.0-dev.aeeb2ca00 → 2.78.1-dev.84a383f51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +7 -7
- package/dist/claude-cli-check.js +64 -37
- package/dist/cli-policy.d.ts +13 -0
- package/dist/cli-policy.js +17 -0
- package/dist/cli.js +95 -55
- package/dist/headless-query.d.ts +22 -0
- package/dist/headless-query.js +24 -4
- package/dist/headless.d.ts +10 -0
- package/dist/headless.js +16 -1
- package/dist/loader.js +7 -10
- package/dist/onboarding.d.ts +10 -0
- package/dist/onboarding.js +2 -2
- package/dist/provider-migrations.d.ts +2 -2
- package/dist/provider-migrations.js +5 -2
- package/dist/resource-loader.d.ts +5 -2
- package/dist/resource-loader.js +28 -5
- package/dist/resources/.managed-resources-content-hash +1 -0
- package/dist/resources/extensions/claude-code-cli/readiness.js +77 -45
- package/dist/resources/extensions/gsd/auto/loop.js +23 -0
- package/dist/resources/extensions/gsd/auto/phases.js +2 -2
- package/dist/resources/extensions/gsd/auto/run-unit.js +3 -1
- package/dist/resources/extensions/gsd/auto/session.js +3 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +43 -4
- package/dist/resources/extensions/gsd/auto-runtime-state.js +31 -0
- package/dist/resources/extensions/gsd/auto-start.js +1 -1
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +2 -2
- package/dist/resources/extensions/gsd/auto-worktree.js +30 -0
- package/dist/resources/extensions/gsd/auto.js +14 -5
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +14 -2
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +7 -5
- package/dist/resources/extensions/gsd/bootstrap/query-tools.js +2 -2
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +5 -4
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +94 -31
- package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +11 -6
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +34 -8
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +38 -2
- package/dist/resources/extensions/gsd/commands/catalog.js +69 -5
- package/dist/resources/extensions/gsd/commands/handlers/core.js +22 -1
- package/dist/resources/extensions/gsd/commands-mcp-status.js +3 -1
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +10 -1
- package/dist/resources/extensions/gsd/dashboard-overlay.js +1 -1
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +4 -0
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +39 -1
- package/dist/resources/extensions/gsd/error-classifier.js +1 -1
- package/dist/resources/extensions/gsd/forensics.js +2 -2
- package/dist/resources/extensions/gsd/git-service.js +12 -5
- package/dist/resources/extensions/gsd/gsd-db.js +11 -2
- package/dist/resources/extensions/gsd/guided-flow.js +23 -23
- package/dist/resources/extensions/gsd/memory-store.js +66 -31
- package/dist/resources/extensions/gsd/milestone-id-reservation.js +36 -0
- package/dist/resources/extensions/gsd/model-router.js +114 -9
- package/dist/resources/extensions/gsd/native-git-bridge.js +7 -1
- package/dist/resources/extensions/gsd/preferences-models.js +91 -15
- package/dist/resources/extensions/gsd/preferences-types.js +2 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +32 -0
- package/dist/resources/extensions/gsd/preferences.js +5 -3
- package/dist/resources/extensions/gsd/prompt-loader.js +23 -12
- package/dist/resources/extensions/gsd/slice-parallel-orchestrator.js +9 -3
- package/dist/resources/extensions/gsd/state.js +42 -0
- package/dist/resources/extensions/gsd/templates/PREFERENCES.md +1 -0
- package/dist/resources/extensions/gsd/tools/memory-tools.js +18 -1
- package/dist/resources/extensions/gsd/visualizer-overlay.js +1 -1
- package/dist/resources/extensions/gsd/watch/header-renderer.js +3 -1
- package/dist/resources/extensions/gsd/worktree-command.js +26 -46
- package/dist/resources/extensions/gsd/worktree-session-state.js +33 -0
- package/dist/resources/extensions/mcp-client/index.js +6 -3
- package/dist/resources/extensions/slash-commands/create-extension.js +36 -22
- package/dist/resources/skills/create-gsd-extension/SKILL.md +9 -5
- package/dist/resources/skills/create-gsd-extension/references/custom-commands.md +1 -1
- package/dist/resources/skills/create-gsd-extension/references/custom-rendering.md +5 -5
- package/dist/resources/skills/create-gsd-extension/references/custom-tools.md +4 -4
- package/dist/resources/skills/create-gsd-extension/references/custom-ui.md +6 -6
- package/dist/resources/skills/create-gsd-extension/references/events-reference.md +3 -3
- package/dist/resources/skills/create-gsd-extension/references/packaging-distribution.md +1 -1
- package/dist/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +3 -3
- package/dist/resources/skills/create-gsd-extension/workflows/create-extension.md +32 -12
- package/dist/rtk-shared.d.ts +3 -0
- package/dist/rtk-shared.js +17 -0
- package/dist/rtk.d.ts +2 -5
- package/dist/rtk.js +3 -20
- package/dist/runtime-checks.d.ts +27 -0
- package/dist/runtime-checks.js +38 -0
- 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 +12 -12
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +44 -4
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +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/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +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 +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +4 -2
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
- 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/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/server/webpack-runtime.js +1 -1
- package/dist/web/standalone/.next/static/chunks/2556.0527fea66e123b7f.js +1 -0
- package/dist/web/standalone/.next/static/chunks/2824.08296bc2f9654698.js +1 -0
- package/dist/web/standalone/.next/static/chunks/3026.3af53b279375f082.js +1 -0
- package/dist/web/standalone/.next/static/chunks/315.6f68ae79b67d25cf.js +1 -0
- package/dist/web/standalone/.next/static/chunks/3497.4bfc60a3b3dea717.js +1 -0
- package/dist/web/standalone/.next/static/chunks/5516.4a07c872b5c3a663.js +1 -0
- package/dist/web/standalone/.next/static/chunks/8336.31b019697882acfb.js +10 -0
- package/dist/web/standalone/.next/static/chunks/8845.c9702695e8c5a9c5.js +2 -0
- package/dist/web/standalone/.next/static/chunks/9058.01ef3a463bda88f1.js +20 -0
- package/dist/web/standalone/.next/static/chunks/9441.1081da1125d1764f.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/{page-5b113fd32bc2a1c3.js → page-9bf2e0c50fb2ca05.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/webpack-f9f0dc45e4f3ac10.js +1 -0
- package/dist/web/standalone/package.json +2 -1
- package/dist/worktree-status-banner.d.ts +1 -0
- package/dist/worktree-status-banner.js +132 -0
- package/package.json +1 -1
- package/packages/daemon/package.json +2 -2
- package/packages/mcp-server/dist/alias-telemetry.d.ts +8 -0
- package/packages/mcp-server/dist/alias-telemetry.d.ts.map +1 -0
- package/packages/mcp-server/dist/alias-telemetry.js +30 -0
- package/packages/mcp-server/dist/alias-telemetry.js.map +1 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +74 -46
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +2 -2
- package/packages/mcp-server/src/alias-telemetry.test.ts +78 -0
- package/packages/mcp-server/src/alias-telemetry.ts +30 -0
- package/packages/mcp-server/src/workflow-tools.test.ts +26 -0
- package/packages/mcp-server/src/workflow-tools.ts +93 -58
- package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.d.ts +2 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.js +231 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.cache-breakpoint.test.js.map +1 -0
- package/packages/pi-ai/dist/providers/anthropic-shared.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic-shared.js +48 -19
- package/packages/pi-ai/dist/providers/anthropic-shared.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +13 -0
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/dist/utils/repair-tool-json.d.ts.map +1 -1
- package/packages/pi-ai/dist/utils/repair-tool-json.js +24 -3
- package/packages/pi-ai/dist/utils/repair-tool-json.js.map +1 -1
- package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js +26 -0
- package/packages/pi-ai/dist/utils/tests/repair-tool-json.test.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-ai/src/providers/anthropic-shared.cache-breakpoint.test.ts +289 -0
- package/packages/pi-ai/src/providers/anthropic-shared.ts +52 -20
- package/packages/pi-ai/src/types.ts +13 -0
- package/packages/pi-ai/src/utils/repair-tool-json.ts +24 -3
- package/packages/pi-ai/src/utils/tests/repair-tool-json.test.ts +32 -0
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +6 -0
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/messages.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/messages.js +4 -0
- package/packages/pi-coding-agent/dist/core/messages.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js +19 -2
- package/packages/pi-coding-agent/dist/core/model-registry-auth-mode.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts +10 -0
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +18 -0
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts +13 -0
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +20 -16
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/token-telemetry.d.ts +37 -0
- package/packages/pi-coding-agent/dist/core/token-telemetry.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/token-telemetry.js +49 -0
- package/packages/pi-coding-agent/dist/core/token-telemetry.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +133 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +14 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.js +78 -0
- package/packages/pi-coding-agent/dist/tests/system-prompt-cache-stability.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/tests/token-telemetry.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/tests/token-telemetry.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/tests/token-telemetry.test.js +181 -0
- package/packages/pi-coding-agent/dist/tests/token-telemetry.test.js.map +1 -0
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +7 -0
- package/packages/pi-coding-agent/src/core/messages.ts +4 -0
- package/packages/pi-coding-agent/src/core/model-registry-auth-mode.test.ts +32 -2
- package/packages/pi-coding-agent/src/core/model-registry.ts +21 -0
- package/packages/pi-coding-agent/src/core/system-prompt.ts +33 -15
- package/packages/pi-coding-agent/src/core/token-telemetry.ts +77 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +212 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +17 -1
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +1 -1
- package/packages/pi-coding-agent/src/tests/system-prompt-cache-stability.test.ts +102 -0
- package/packages/pi-coding-agent/src/tests/token-telemetry.test.ts +200 -0
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-tui/dist/__tests__/autocomplete.test.js +17 -3
- package/packages/pi-tui/dist/__tests__/autocomplete.test.js.map +1 -1
- package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.d.ts +2 -0
- package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.js +161 -0
- package/packages/pi-tui/dist/components/__tests__/leak-fixes-runtime.test.js.map +1 -0
- package/packages/pi-tui/package.json +1 -1
- package/packages/pi-tui/src/__tests__/autocomplete.test.ts +20 -3
- package/packages/pi-tui/src/components/__tests__/leak-fixes-runtime.test.ts +219 -0
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
- package/packages/rpc-client/package.json +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/claude-code-cli/readiness.ts +78 -46
- package/src/resources/extensions/gsd/auto/loop.ts +24 -2
- package/src/resources/extensions/gsd/auto/phases.ts +3 -3
- package/src/resources/extensions/gsd/auto/run-unit.ts +3 -1
- package/src/resources/extensions/gsd/auto/session.ts +3 -0
- package/src/resources/extensions/gsd/auto/types.ts +1 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +46 -8
- package/src/resources/extensions/gsd/auto-runtime-state.ts +51 -0
- package/src/resources/extensions/gsd/auto-start.ts +1 -1
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +2 -4
- package/src/resources/extensions/gsd/auto-worktree.ts +38 -0
- package/src/resources/extensions/gsd/auto.ts +14 -4
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +15 -13
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +8 -7
- package/src/resources/extensions/gsd/bootstrap/query-tools.ts +2 -2
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +10 -9
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +102 -31
- package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +12 -6
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +39 -8
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +39 -11
- package/src/resources/extensions/gsd/commands/catalog.ts +75 -5
- package/src/resources/extensions/gsd/commands/handlers/core.ts +22 -1
- package/src/resources/extensions/gsd/commands-mcp-status.ts +3 -1
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +15 -1
- package/src/resources/extensions/gsd/dashboard-overlay.ts +1 -1
- package/src/resources/extensions/gsd/docs/preferences-reference.md +4 -0
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +39 -1
- package/src/resources/extensions/gsd/doctor-types.ts +3 -1
- package/src/resources/extensions/gsd/error-classifier.ts +1 -1
- package/src/resources/extensions/gsd/forensics.ts +2 -2
- package/src/resources/extensions/gsd/git-service.ts +13 -5
- package/src/resources/extensions/gsd/gsd-db.ts +12 -2
- package/src/resources/extensions/gsd/guided-flow.ts +25 -25
- package/src/resources/extensions/gsd/memory-store.ts +81 -28
- package/src/resources/extensions/gsd/milestone-id-reservation.ts +47 -0
- package/src/resources/extensions/gsd/model-router.ts +172 -9
- package/src/resources/extensions/gsd/native-git-bridge.ts +7 -1
- package/src/resources/extensions/gsd/preferences-models.ts +101 -15
- package/src/resources/extensions/gsd/preferences-types.ts +6 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +35 -0
- package/src/resources/extensions/gsd/preferences.ts +16 -2
- package/src/resources/extensions/gsd/prompt-loader.ts +26 -12
- package/src/resources/extensions/gsd/slice-parallel-orchestrator.ts +9 -3
- package/src/resources/extensions/gsd/state.ts +42 -0
- package/src/resources/extensions/gsd/templates/PREFERENCES.md +1 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +178 -1
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +58 -0
- package/src/resources/extensions/gsd/tests/auto-session-encapsulation.test.ts +9 -5
- package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +21 -4
- package/src/resources/extensions/gsd/tests/bootstrap-derive-state-db-open.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/budget-prediction.test.ts +138 -211
- package/src/resources/extensions/gsd/tests/complete-slice-verification-gate.test.ts +142 -59
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +7 -4
- package/src/resources/extensions/gsd/tests/completed-at-reconcile.test.ts +89 -32
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +41 -23
- package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +3 -43
- package/src/resources/extensions/gsd/tests/debug-logger.test.ts +5 -3
- package/src/resources/extensions/gsd/tests/deferred-milestone-dir-4996.test.ts +116 -0
- package/src/resources/extensions/gsd/tests/discuss-empty-db-fallback.test.ts +22 -87
- package/src/resources/extensions/gsd/tests/discuss-queued-milestones.test.ts +7 -118
- package/src/resources/extensions/gsd/tests/discuss-tool-scope-leak.test.ts +18 -60
- package/src/resources/extensions/gsd/tests/doctor-orphan-milestone-4996.test.ts +100 -0
- package/src/resources/extensions/gsd/tests/double-merge-guard.test.ts +14 -76
- package/src/resources/extensions/gsd/tests/ensure-preconditions-guard-4996.test.ts +93 -0
- package/src/resources/extensions/gsd/tests/false-degraded-mode-warning.test.ts +22 -83
- package/src/resources/extensions/gsd/tests/finalize-timeout-guard.test.ts +1 -63
- package/src/resources/extensions/gsd/tests/find-missing-summaries-closed-runtime.test.ts +47 -0
- package/src/resources/extensions/gsd/tests/forensics-stuck-loops.test.ts +26 -1
- package/src/resources/extensions/gsd/tests/gitignore-bg-shell-runtime.test.ts +63 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +30 -0
- package/src/resources/extensions/gsd/tests/gsd-no-project-error-runtime.test.ts +81 -0
- package/src/resources/extensions/gsd/tests/headless-answers.test.ts +14 -4
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +22 -12
- package/src/resources/extensions/gsd/tests/help-menu-coverage.test.ts +57 -0
- package/src/resources/extensions/gsd/tests/import-done-milestones-runtime.test.ts +145 -0
- package/src/resources/extensions/gsd/tests/init-prefs-routing.test.ts +64 -1
- package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/integration/token-savings.test.ts +0 -23
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +128 -0
- package/src/resources/extensions/gsd/tests/memory-tools.test.ts +33 -1
- package/src/resources/extensions/gsd/tests/merge-self-branch-guard.test.ts +124 -0
- package/src/resources/extensions/gsd/tests/milestone-id-gap-reuse-4996.test.ts +152 -0
- package/src/resources/extensions/gsd/tests/model-router.test.ts +169 -8
- package/src/resources/extensions/gsd/tests/native-git-infra-errors.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/orphaned-worktree-audit.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +32 -43
- package/src/resources/extensions/gsd/tests/phases-merge-error-stops-auto.test.ts +4 -10
- package/src/resources/extensions/gsd/tests/preferences.test.ts +127 -0
- package/src/resources/extensions/gsd/tests/prompt-step-ordering.test.ts +16 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/quick-turn-end-cleanup.test.ts +6 -6
- package/src/resources/extensions/gsd/tests/register-hooks-compaction-checkpoint.test.ts +93 -0
- package/src/resources/extensions/gsd/tests/session-start-footer.test.ts +168 -19
- package/src/resources/extensions/gsd/tests/slice-parallel-orchestrator.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/smart-entry-complete.test.ts +23 -1
- package/src/resources/extensions/gsd/tests/system-context-message-routing.test.ts +101 -0
- package/src/resources/extensions/gsd/tests/token-profile.test.ts +51 -4
- package/src/resources/extensions/gsd/tests/turn-epoch.test.ts +7 -16
- package/src/resources/extensions/gsd/tests/unstructured-continue-context-injection.test.ts +5 -7
- package/src/resources/extensions/gsd/tests/uok-gitops-turn-action.test.ts +15 -1
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +6 -6
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +15 -0
- package/src/resources/extensions/gsd/tools/memory-tools.ts +17 -1
- package/src/resources/extensions/gsd/unit-context-manifest.ts +8 -8
- package/src/resources/extensions/gsd/visualizer-overlay.ts +1 -1
- package/src/resources/extensions/gsd/watch/header-renderer.ts +3 -1
- package/src/resources/extensions/gsd/workflow-logger.ts +1 -0
- package/src/resources/extensions/gsd/worktree-command.ts +31 -44
- package/src/resources/extensions/gsd/worktree-session-state.ts +35 -0
- package/src/resources/extensions/mcp-client/index.ts +6 -3
- package/src/resources/extensions/mcp-client/tests/global-config.test.ts +91 -0
- package/src/resources/extensions/slash-commands/create-extension.ts +38 -24
- package/src/resources/skills/create-gsd-extension/SKILL.md +9 -5
- package/src/resources/skills/create-gsd-extension/references/custom-commands.md +1 -1
- package/src/resources/skills/create-gsd-extension/references/custom-rendering.md +5 -5
- package/src/resources/skills/create-gsd-extension/references/custom-tools.md +4 -4
- package/src/resources/skills/create-gsd-extension/references/custom-ui.md +6 -6
- package/src/resources/skills/create-gsd-extension/references/events-reference.md +3 -3
- package/src/resources/skills/create-gsd-extension/references/packaging-distribution.md +1 -1
- package/src/resources/skills/create-gsd-extension/references/remote-execution-overrides.md +3 -3
- package/src/resources/skills/create-gsd-extension/templates/extension-skeleton.ts +2 -2
- package/src/resources/skills/create-gsd-extension/templates/stateful-tool-skeleton.ts +3 -3
- package/src/resources/skills/create-gsd-extension/templates/templates.test.ts +58 -0
- package/src/resources/skills/create-gsd-extension/workflows/create-extension.md +32 -12
- package/dist/resources/extensions/browser-tools/tests/browser-tools-integration.test.mjs +0 -601
- package/dist/resources/extensions/browser-tools/tests/browser-tools-unit.test.cjs +0 -651
- package/dist/resources/extensions/browser-tools/tests/capture-sharp-optional.test.cjs +0 -91
- package/dist/resources/extensions/gsd/tests/auto-supervisor.test.mjs +0 -53
- package/dist/resources/extensions/gsd/tests/dist-redirect.mjs +0 -112
- package/dist/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +0 -23
- package/dist/resources/extensions/gsd/tests/resolve-ts.mjs +0 -5
- package/dist/resources/skills/github-workflows/references/gh/tests/__init__.py +0 -0
- package/dist/resources/skills/github-workflows/references/gh/tests/test_github_project_setup.py +0 -608
- package/dist/web/standalone/.next/static/chunks/2826.e9f5195e91f9cad2.js +0 -11
- package/dist/web/standalone/.next/static/chunks/3621.fc7480022c972438.js +0 -20
- package/dist/web/standalone/.next/static/chunks/webpack-2e68521d7c82f7c2.js +0 -1
- package/src/resources/extensions/gsd/tests/copy-planning-artifacts-samepath.test.ts +0 -22
- package/src/resources/extensions/gsd/tests/discuss-slice-structured-questions.test.ts +0 -47
- package/src/resources/extensions/gsd/tests/empty-content-abort-loop.test.ts +0 -75
- /package/dist/web/standalone/.next/static/{cAJH99yNS1UPbeSEiNRrV → UF5VF4F1tB0miEtJS7LyX}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{cAJH99yNS1UPbeSEiNRrV → UF5VF4F1tB0miEtJS7LyX}/_ssgManifest.js +0 -0
|
@@ -1,42 +1,99 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* reconciled to 'complete' via the file-existence path in state.ts.
|
|
2
|
+
* Behavioural regression test for #4129.
|
|
4
3
|
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* When deriveStateFromDb's reconcileSliceTasks finds a SUMMARY.md on disk
|
|
5
|
+
* for a task whose DB row is still pending, it flips the row to "complete".
|
|
6
|
+
* Before #4129, the call to updateTaskStatus omitted the completedAt
|
|
7
|
+
* timestamp, leaving completed_at NULL forever.
|
|
8
8
|
*
|
|
9
|
-
*
|
|
9
|
+
* The fix passes new Date().toISOString() as the 5th argument; this test
|
|
10
|
+
* exercises that path end-to-end and asserts the column is populated.
|
|
11
|
+
*
|
|
12
|
+
* Refs #4829 (rewrite from positional source-grep).
|
|
10
13
|
*/
|
|
11
14
|
|
|
12
|
-
import { describe, test } from
|
|
13
|
-
import assert from
|
|
14
|
-
import {
|
|
15
|
-
import { join
|
|
16
|
-
import {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
15
|
+
import { describe, test, beforeEach, afterEach } from 'node:test';
|
|
16
|
+
import assert from 'node:assert/strict';
|
|
17
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from 'node:fs';
|
|
18
|
+
import { join } from 'node:path';
|
|
19
|
+
import { tmpdir } from 'node:os';
|
|
20
|
+
|
|
21
|
+
import { deriveStateFromDb, invalidateStateCache } from '../state.ts';
|
|
22
|
+
import {
|
|
23
|
+
openDatabase,
|
|
24
|
+
closeDatabase,
|
|
25
|
+
insertMilestone,
|
|
26
|
+
insertSlice,
|
|
27
|
+
insertTask,
|
|
28
|
+
getTask,
|
|
29
|
+
} from '../gsd-db.ts';
|
|
30
|
+
|
|
31
|
+
let basePath: string;
|
|
32
|
+
|
|
33
|
+
function setupProject(): void {
|
|
34
|
+
basePath = mkdtempSync(join(tmpdir(), 'gsd-completed-at-'));
|
|
35
|
+
// Project structure with active milestone, one slice, one task whose
|
|
36
|
+
// SUMMARY.md is already on disk — but the DB row is still "pending".
|
|
37
|
+
mkdirSync(join(basePath, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'tasks'), { recursive: true });
|
|
38
|
+
|
|
39
|
+
// CONTEXT + ROADMAP so deriveState identifies M001 as active and S01 as the active slice.
|
|
40
|
+
writeFileSync(
|
|
41
|
+
join(basePath, '.gsd', 'milestones', 'M001', 'M001-CONTEXT.md'),
|
|
42
|
+
'# M001\nActive milestone.\n',
|
|
43
|
+
);
|
|
44
|
+
writeFileSync(
|
|
45
|
+
join(basePath, '.gsd', 'milestones', 'M001', 'M001-ROADMAP.md'),
|
|
46
|
+
`# M001\n\n## Slices\n\n- [ ] **S01: Slice** \`risk:low\` \`depends:[]\`\n - After this: works\n`,
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
// Plan file for the slice so reconcile can populate task list if DB is empty.
|
|
50
|
+
writeFileSync(
|
|
51
|
+
join(basePath, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'S01-PLAN.md'),
|
|
52
|
+
`# S01: Slice\n\n## Tasks\n\n- [ ] **T01: Test task** \`est:30m\`\n - Do: x\n - Verify: y\n`,
|
|
53
|
+
);
|
|
54
|
+
|
|
55
|
+
// The summary file: this is the on-disk evidence that flips the task
|
|
56
|
+
// status to "complete" inside reconcileSliceTasks.
|
|
57
|
+
writeFileSync(
|
|
58
|
+
join(basePath, '.gsd', 'milestones', 'M001', 'slices', 'S01', 'tasks', 'T01-SUMMARY.md'),
|
|
59
|
+
'---\nid: T01\nparent: S01\nmilestone: M001\nblocker_discovered: false\n---\n# T01\n',
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
describe('completed_at reconcile (#4129)', () => {
|
|
64
|
+
beforeEach(() => {
|
|
65
|
+
setupProject();
|
|
66
|
+
openDatabase(join(basePath, '.gsd', 'gsd.db'));
|
|
67
|
+
insertMilestone({ id: 'M001', title: 'M001', status: 'active' });
|
|
68
|
+
insertSlice({ id: 'S01', milestoneId: 'M001', title: 'Slice', status: 'active' });
|
|
69
|
+
// Task is "pending" in DB, but SUMMARY.md exists on disk → reconcile flips it.
|
|
70
|
+
insertTask({ id: 'T01', sliceId: 'S01', milestoneId: 'M001', title: 'Test task', status: 'pending' });
|
|
71
|
+
invalidateStateCache();
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
afterEach(() => {
|
|
75
|
+
closeDatabase();
|
|
76
|
+
try { rmSync(basePath, { recursive: true, force: true }); } catch { /* */ }
|
|
32
77
|
});
|
|
33
78
|
|
|
34
|
-
test(
|
|
35
|
-
|
|
36
|
-
assert.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
79
|
+
test('reconcileSliceTasks sets completed_at when flipping a pending task to complete via SUMMARY.md', async () => {
|
|
80
|
+
const before = getTask('M001', 'S01', 'T01');
|
|
81
|
+
assert.strictEqual(before?.status, 'pending', 'task starts pending');
|
|
82
|
+
assert.strictEqual(before?.completed_at, null, 'task starts with completed_at NULL');
|
|
83
|
+
|
|
84
|
+
// Trigger the reconcile path (state.ts → reconcileSliceTasks).
|
|
85
|
+
await deriveStateFromDb(basePath);
|
|
86
|
+
|
|
87
|
+
const after = getTask('M001', 'S01', 'T01');
|
|
88
|
+
assert.strictEqual(after?.status, 'complete', 'task should be flipped to complete');
|
|
89
|
+
assert.ok(
|
|
90
|
+
typeof after?.completed_at === 'string' && after.completed_at.length > 0,
|
|
91
|
+
`completed_at must be populated by reconcileSliceTasks (#4129); got ${JSON.stringify(after?.completed_at)}`,
|
|
92
|
+
);
|
|
93
|
+
// Sanity: timestamp parses as a valid ISO date.
|
|
94
|
+
assert.ok(
|
|
95
|
+
!Number.isNaN(Date.parse(after!.completed_at!)),
|
|
96
|
+
`completed_at should be a valid ISO timestamp, got ${after!.completed_at}`,
|
|
40
97
|
);
|
|
41
98
|
});
|
|
42
99
|
});
|
|
@@ -12,7 +12,7 @@ import { mkdtempSync, rmSync, existsSync } from "node:fs";
|
|
|
12
12
|
import { join } from "node:path";
|
|
13
13
|
import { tmpdir } from "node:os";
|
|
14
14
|
|
|
15
|
-
import { autoLoop, resolveAgentEnd, _resetPendingResolve } from "../auto-loop.js";
|
|
15
|
+
import { autoLoop, resolveAgentEnd, _hasPendingResolveForTest, _resetPendingResolve } from "../auto-loop.js";
|
|
16
16
|
import type { LoopDeps } from "../auto/loop-deps.js";
|
|
17
17
|
import type { SessionLockStatus } from "../session-lock.js";
|
|
18
18
|
import { writeGraph, readGraph, type WorkflowGraph, type GraphStep } from "../graph.ts";
|
|
@@ -29,6 +29,17 @@ function makeTmpDir(): string {
|
|
|
29
29
|
return dir;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
async function resolveNextAgentEnd(timeoutMs = 3_000): Promise<void> {
|
|
33
|
+
const deadline = Date.now() + timeoutMs;
|
|
34
|
+
while (!_hasPendingResolveForTest()) {
|
|
35
|
+
if (Date.now() > deadline) {
|
|
36
|
+
throw new Error("Timed out waiting for pending agent_end resolver");
|
|
37
|
+
}
|
|
38
|
+
await new Promise((r) => setTimeout(r, 5));
|
|
39
|
+
}
|
|
40
|
+
resolveAgentEnd({ messages: [{ role: "assistant" }] });
|
|
41
|
+
}
|
|
42
|
+
|
|
32
43
|
afterEach(() => {
|
|
33
44
|
_resetPendingResolve();
|
|
34
45
|
for (const d of tmpDirs) {
|
|
@@ -276,19 +287,16 @@ describe("Custom engine loop integration", () => {
|
|
|
276
287
|
// We need to resolve resolveAgentEnd for each step.
|
|
277
288
|
|
|
278
289
|
// Step 1: step-a
|
|
279
|
-
await new Promise((r) => setTimeout(r, 80));
|
|
280
290
|
unitCount++;
|
|
281
|
-
|
|
291
|
+
await resolveNextAgentEnd();
|
|
282
292
|
|
|
283
293
|
// Step 2: step-b
|
|
284
|
-
await new Promise((r) => setTimeout(r, 80));
|
|
285
294
|
unitCount++;
|
|
286
|
-
|
|
295
|
+
await resolveNextAgentEnd();
|
|
287
296
|
|
|
288
297
|
// Step 3: step-c
|
|
289
|
-
await new Promise((r) => setTimeout(r, 80));
|
|
290
298
|
unitCount++;
|
|
291
|
-
|
|
299
|
+
await resolveNextAgentEnd();
|
|
292
300
|
|
|
293
301
|
// After step-c completes, engine.reconcile marks it complete, then
|
|
294
302
|
// next deriveState sees isComplete=true → stopAuto → loop exits
|
|
@@ -398,8 +406,7 @@ describe("Custom engine loop integration", () => {
|
|
|
398
406
|
|
|
399
407
|
const loopPromise = autoLoop(ctx, pi, s, deps);
|
|
400
408
|
|
|
401
|
-
await
|
|
402
|
-
resolveAgentEnd({ messages: [{ role: "assistant" }] });
|
|
409
|
+
await resolveNextAgentEnd();
|
|
403
410
|
|
|
404
411
|
await loopPromise;
|
|
405
412
|
|
|
@@ -460,12 +467,10 @@ describe("Custom engine loop integration", () => {
|
|
|
460
467
|
const loopPromise = autoLoop(ctx, pi, s, deps);
|
|
461
468
|
|
|
462
469
|
// Resolve step-a
|
|
463
|
-
await
|
|
464
|
-
resolveAgentEnd({ messages: [{ role: "assistant" }] });
|
|
470
|
+
await resolveNextAgentEnd();
|
|
465
471
|
|
|
466
472
|
// Resolve step-b
|
|
467
|
-
await
|
|
468
|
-
resolveAgentEnd({ messages: [{ role: "assistant" }] });
|
|
473
|
+
await resolveNextAgentEnd();
|
|
469
474
|
|
|
470
475
|
await loopPromise;
|
|
471
476
|
|
|
@@ -514,7 +519,9 @@ describe("Custom engine loop integration", () => {
|
|
|
514
519
|
});
|
|
515
520
|
|
|
516
521
|
const resolver = setInterval(() => {
|
|
517
|
-
|
|
522
|
+
if (_hasPendingResolveForTest()) {
|
|
523
|
+
resolveAgentEnd({ messages: [{ role: "assistant" }] });
|
|
524
|
+
}
|
|
518
525
|
}, 25);
|
|
519
526
|
let timeout: NodeJS.Timeout | undefined;
|
|
520
527
|
try {
|
|
@@ -569,7 +576,9 @@ describe("Custom engine loop integration", () => {
|
|
|
569
576
|
});
|
|
570
577
|
const deps1 = makeMockDeps();
|
|
571
578
|
const resolver1 = setInterval(() => {
|
|
572
|
-
|
|
579
|
+
if (_hasPendingResolveForTest()) {
|
|
580
|
+
resolveAgentEnd({ messages: [{ role: "assistant" }] });
|
|
581
|
+
}
|
|
573
582
|
if (pi1.calls.length >= 2) {
|
|
574
583
|
s1.active = false;
|
|
575
584
|
}
|
|
@@ -614,7 +623,9 @@ describe("Custom engine loop integration", () => {
|
|
|
614
623
|
},
|
|
615
624
|
});
|
|
616
625
|
const resolver2 = setInterval(() => {
|
|
617
|
-
|
|
626
|
+
if (_hasPendingResolveForTest()) {
|
|
627
|
+
resolveAgentEnd({ messages: [{ role: "assistant" }] });
|
|
628
|
+
}
|
|
618
629
|
}, 25);
|
|
619
630
|
let timeout2: NodeJS.Timeout | undefined;
|
|
620
631
|
try {
|
|
@@ -640,7 +651,12 @@ describe("Custom engine loop integration", () => {
|
|
|
640
651
|
assert.match(stopEntry ?? "", /requested retry 4 times without passing/);
|
|
641
652
|
});
|
|
642
653
|
|
|
643
|
-
it("
|
|
654
|
+
it("two-step workflow drives both steps to complete and stops when isComplete fires", async () => {
|
|
655
|
+
// Note (#4831): renamed from "GRAPH.yaml step stays pending when session
|
|
656
|
+
// deactivates before reconcile" — the assertion body never proved the
|
|
657
|
+
// pending-on-deactivate claim and even comments that "the reconcile
|
|
658
|
+
// will still run for step-b". The behaviour this test actually pins is:
|
|
659
|
+
// both steps reconcile complete and stopAuto fires once isComplete.
|
|
644
660
|
_resetPendingResolve();
|
|
645
661
|
|
|
646
662
|
// Two-step workflow: a → b. We will complete step-a, then force a break
|
|
@@ -672,28 +688,30 @@ describe("Custom engine loop integration", () => {
|
|
|
672
688
|
const loopPromise = autoLoop(ctx, pi, s, deps);
|
|
673
689
|
|
|
674
690
|
// Resolve step-a successfully
|
|
675
|
-
await
|
|
676
|
-
resolveAgentEnd({ messages: [{ role: "assistant" }] });
|
|
691
|
+
await resolveNextAgentEnd();
|
|
677
692
|
|
|
678
693
|
// Step-b enters runUnit — deactivate the session before resolving.
|
|
679
694
|
// runUnit checks s.active after newSession and returns cancelled if false.
|
|
680
695
|
// But since newSession resolves synchronously in our mock (before the
|
|
681
696
|
// active check), the unit still runs. Instead, let's just cancel it.
|
|
682
|
-
await new Promise((r) => setTimeout(r, 80));
|
|
683
697
|
// Resolve as cancelled to simulate a failed session
|
|
684
|
-
|
|
698
|
+
await resolveNextAgentEnd();
|
|
685
699
|
|
|
686
700
|
// The reconcile will still run for step-b in this flow since
|
|
687
701
|
// runUnitPhase returns "next" (not "break") for completed units.
|
|
688
702
|
// After both steps complete, the engine detects isComplete and stops.
|
|
689
703
|
await loopPromise;
|
|
690
704
|
|
|
691
|
-
//
|
|
705
|
+
// Both steps reconcile complete; the renamed expectation pins that the
|
|
706
|
+
// engine drives the workflow through isComplete rather than leaving any
|
|
707
|
+
// step pending.
|
|
692
708
|
const finalGraph = readGraph(runDir);
|
|
693
709
|
const stepA = finalGraph.steps.find(s => s.id === "step-a");
|
|
710
|
+
const stepB = finalGraph.steps.find(s => s.id === "step-b");
|
|
694
711
|
assert.equal(stepA?.status, "complete", "Step-a should be complete");
|
|
712
|
+
assert.equal(stepB?.status, "complete", "Step-b should be complete");
|
|
695
713
|
|
|
696
|
-
//
|
|
714
|
+
// The loop must stop once isComplete fires.
|
|
697
715
|
assert.ok(
|
|
698
716
|
deps.callLog.some((e: string) => e.startsWith("stopAuto:")),
|
|
699
717
|
"stopAuto should have been called",
|
|
@@ -16,7 +16,6 @@
|
|
|
16
16
|
* retrying can never succeed and causes cost spikes.
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import { readFileSync } from "node:fs";
|
|
20
19
|
import { join, sep } from "node:path";
|
|
21
20
|
import { createTestContext } from "./test-helpers.ts";
|
|
22
21
|
|
|
@@ -89,47 +88,8 @@ assertEq(
|
|
|
89
88
|
"Non-worktree path is unchanged",
|
|
90
89
|
);
|
|
91
90
|
|
|
92
|
-
//
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const dynamicToolsSrc = readFileSync(
|
|
97
|
-
join(import.meta.dirname, "..", "bootstrap", "dynamic-tools.ts"),
|
|
98
|
-
"utf-8",
|
|
99
|
-
);
|
|
100
|
-
|
|
101
|
-
// ensureDbOpen should surface diagnostic context, not just boolean false
|
|
102
|
-
// Check that the catch block logs error details via workflow-logger
|
|
103
|
-
assertTrue(
|
|
104
|
-
dynamicToolsSrc.includes("ensureDbOpen failed") && dynamicToolsSrc.includes("logWarning"),
|
|
105
|
-
"ensureDbOpen catch block surfaces diagnostic information via logWarning instead of bare false (#2517)",
|
|
106
|
-
);
|
|
107
|
-
|
|
108
|
-
// ── Part 3: post-unit does NOT artifact-retry on db_unavailable ──────────
|
|
109
|
-
|
|
110
|
-
console.log("\n=== #2517 Part 3: post-unit db_unavailable is infra-fatal ===");
|
|
111
|
-
|
|
112
|
-
const postUnitSrc = readFileSync(
|
|
113
|
-
join(import.meta.dirname, "..", "auto-post-unit.ts"),
|
|
114
|
-
"utf-8",
|
|
115
|
-
);
|
|
116
|
-
|
|
117
|
-
// The artifact retry block should check DB availability and skip retry
|
|
118
|
-
// when the DB is unavailable (infra failure, not a missing artifact).
|
|
119
|
-
assertTrue(
|
|
120
|
-
postUnitSrc.includes("db_unavailable") || postUnitSrc.includes("isDbAvailable"),
|
|
121
|
-
"post-unit artifact retry path checks DB availability to avoid retry loop (#2517)",
|
|
122
|
-
);
|
|
123
|
-
|
|
124
|
-
// Verify the retry block is guarded: when !isDbAvailable(), the code must
|
|
125
|
-
// NOT return "retry". The pattern should be: if (!verified && !isDbAvailable()) { skip }
|
|
126
|
-
// followed by else if (!verified) { ... return "retry" }
|
|
127
|
-
const dbUnavailableGuard = postUnitSrc.match(
|
|
128
|
-
/!triggerArtifactVerified\s*&&\s*!isDbAvailable\(\)/,
|
|
129
|
-
);
|
|
130
|
-
assertTrue(
|
|
131
|
-
!!dbUnavailableGuard,
|
|
132
|
-
"The retry block explicitly guards against !isDbAvailable() before returning 'retry' (#2517)",
|
|
133
|
-
);
|
|
91
|
+
// Source-grep checks for ensureDbOpen diagnostics + post-unit retry guard
|
|
92
|
+
// were removed (#4826) — the behavioural retry-loop tests live in
|
|
93
|
+
// auto-post-unit.test.ts and exercise isDbAvailable() directly.
|
|
134
94
|
|
|
135
95
|
report();
|
|
@@ -167,9 +167,11 @@ test('auto-prunes old debug logs', () => {
|
|
|
167
167
|
enableDebug(tmp);
|
|
168
168
|
|
|
169
169
|
const files = readdirSync(debugDir).filter(f => f.startsWith('debug-') && f.endsWith('.log'));
|
|
170
|
-
//
|
|
171
|
-
//
|
|
172
|
-
|
|
170
|
+
// MAX_DEBUG_LOGS is 5 — enableDebug prunes to < 5 old and then creates 1 new,
|
|
171
|
+
// so the directory must hold at most 5 files in total. The previous
|
|
172
|
+
// assertion (<= 6) would have passed even with one stale log left behind
|
|
173
|
+
// (Refs #4831).
|
|
174
|
+
assert.ok(files.length <= 5, `should have pruned old logs to <= 5, got ${files.length}`);
|
|
173
175
|
|
|
174
176
|
disableDebug();
|
|
175
177
|
});
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
// GSD Extension — Regression test for #4996: deferred milestone dir creation
|
|
2
|
+
// Verifies that showHeadlessMilestoneCreation does not pre-create the milestone
|
|
3
|
+
// directory before the discuss flow runs. The dir should only appear after a
|
|
4
|
+
// writer (saveArtifactToDb / atomicWriteAsync) emits the first artifact.
|
|
5
|
+
|
|
6
|
+
import { describe, it, beforeEach, afterEach } from "node:test";
|
|
7
|
+
import assert from "node:assert/strict";
|
|
8
|
+
import { mkdtempSync, mkdirSync, existsSync, rmSync, readFileSync } from "node:fs";
|
|
9
|
+
import { dirname, join } from "node:path";
|
|
10
|
+
import { tmpdir } from "node:os";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
|
+
|
|
13
|
+
import { isReusableGhostMilestone } from "../state.ts";
|
|
14
|
+
import { nextMilestoneIdReserved } from "../milestone-id-reservation.ts";
|
|
15
|
+
import { clearReservedMilestoneIds, findMilestoneIds } from "../milestone-ids.ts";
|
|
16
|
+
import { invalidateAllCaches } from "../cache.ts";
|
|
17
|
+
import { closeDatabase, openDatabase } from "../gsd-db.ts";
|
|
18
|
+
|
|
19
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
20
|
+
const GUIDED_FLOW_PATH = join(__dirname, "..", "guided-flow.ts");
|
|
21
|
+
|
|
22
|
+
function getShowHeadlessBody(): string {
|
|
23
|
+
const source = readFileSync(GUIDED_FLOW_PATH, "utf-8");
|
|
24
|
+
const fnStart = source.indexOf("export async function showHeadlessMilestoneCreation");
|
|
25
|
+
assert.ok(fnStart > -1, "showHeadlessMilestoneCreation must exist in guided-flow.ts");
|
|
26
|
+
const nextExport = source.indexOf("\nexport ", fnStart + 1);
|
|
27
|
+
return source.slice(fnStart, nextExport === -1 ? source.length : nextExport);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function makeBase(prefix = "gsd-deferred-dir-"): string {
|
|
31
|
+
const base = mkdtempSync(join(tmpdir(), prefix));
|
|
32
|
+
mkdirSync(join(base, ".gsd", "milestones"), { recursive: true });
|
|
33
|
+
return base;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
describe("showHeadlessMilestoneCreation source guard (#4996)", () => {
|
|
37
|
+
it("does not call mkdirSync in the headless milestone creation path", () => {
|
|
38
|
+
const body = getShowHeadlessBody();
|
|
39
|
+
assert.doesNotMatch(
|
|
40
|
+
body,
|
|
41
|
+
/\bmkdirSync\s*\(/,
|
|
42
|
+
"showHeadlessMilestoneCreation must not pre-create milestone directories",
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it("does not call mkdir or mkdirp before dispatchWorkflow", () => {
|
|
47
|
+
const body = getShowHeadlessBody();
|
|
48
|
+
const dispatchIdx = body.indexOf("dispatchWorkflow");
|
|
49
|
+
assert.ok(dispatchIdx > -1, "dispatchWorkflow must be present");
|
|
50
|
+
|
|
51
|
+
const beforeDispatch = body.slice(0, dispatchIdx);
|
|
52
|
+
assert.doesNotMatch(
|
|
53
|
+
beforeDispatch,
|
|
54
|
+
/\b(?:mkdir|mkdirp)\s*\(/,
|
|
55
|
+
"showHeadlessMilestoneCreation must defer directory creation until artifact write",
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe("deferred milestone dir creation (#4996)", () => {
|
|
61
|
+
let base: string;
|
|
62
|
+
|
|
63
|
+
beforeEach(() => {
|
|
64
|
+
clearReservedMilestoneIds();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
afterEach(() => {
|
|
68
|
+
try { closeDatabase(); } catch { /* ignore */ }
|
|
69
|
+
try { invalidateAllCaches(); } catch { /* ignore */ }
|
|
70
|
+
try { clearReservedMilestoneIds(); } catch { /* ignore */ }
|
|
71
|
+
try { rmSync(base, { recursive: true, force: true }); } catch { /* ignore */ }
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it("(a) fresh project: milestones dir has no M001 entry before any discuss flow", () => {
|
|
75
|
+
base = makeBase();
|
|
76
|
+
const nextId = nextMilestoneIdReserved(findMilestoneIds(base), false, base);
|
|
77
|
+
assert.equal(nextId, "M001", "reservation should choose M001 for a fresh project");
|
|
78
|
+
|
|
79
|
+
const ids = findMilestoneIds(base);
|
|
80
|
+
assert.equal(ids.length, 0, "no milestone dirs should exist before any discuss flow");
|
|
81
|
+
|
|
82
|
+
// And specifically M001 should not exist
|
|
83
|
+
const m001Dir = join(base, ".gsd", "milestones", "M001");
|
|
84
|
+
assert.ok(!existsSync(m001Dir), "M001 dir must not exist before the discuss flow runs");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it("(b) abandoned discuss flow leaves no orphan: isReusableGhostMilestone returns false for non-existent dir", () => {
|
|
88
|
+
base = makeBase();
|
|
89
|
+
const nextId = nextMilestoneIdReserved(findMilestoneIds(base), false, base);
|
|
90
|
+
assert.equal(nextId, "M001", "reservation should not require a pre-created directory");
|
|
91
|
+
|
|
92
|
+
const m001Dir = join(base, ".gsd", "milestones", "M001");
|
|
93
|
+
assert.ok(!existsSync(m001Dir), "no M001 dir should exist");
|
|
94
|
+
assert.equal(isReusableGhostMilestone(base, "M001"), false, "non-existent milestone should not be reusable");
|
|
95
|
+
// findMilestoneIds only returns dirs that exist
|
|
96
|
+
const ids = findMilestoneIds(base);
|
|
97
|
+
assert.ok(!ids.includes("M001"), "M001 should not appear in findMilestoneIds when no dir exists");
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("(c) a stub dir left from a previous bug IS reusable but a newly-generated ID with no dir is not in the ghost list", () => {
|
|
101
|
+
base = makeBase();
|
|
102
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
103
|
+
// Create a stub to represent a pre-existing phantom
|
|
104
|
+
mkdirSync(join(base, ".gsd", "milestones", "M001", "slices"), { recursive: true });
|
|
105
|
+
|
|
106
|
+
// isReusableGhostMilestone identifies the orphaned stub
|
|
107
|
+
assert.ok(isReusableGhostMilestone(base, "M001"), "pre-existing stub should be identified as reusable ghost");
|
|
108
|
+
const nextId = nextMilestoneIdReserved(findMilestoneIds(base), false, base);
|
|
109
|
+
assert.equal(nextId, "M001", "reservation should reuse the pre-existing ghost");
|
|
110
|
+
|
|
111
|
+
// The new ID (M002, which would be max+1 in this scenario but ghost reuse returns M001)
|
|
112
|
+
// should not have a dir
|
|
113
|
+
const m002Dir = join(base, ".gsd", "milestones", "M002");
|
|
114
|
+
assert.ok(!existsSync(m002Dir), "a freshly-requested ID should have no dir until first artifact write");
|
|
115
|
+
});
|
|
116
|
+
});
|
|
@@ -1,28 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Behavioural regression tests for #2892.
|
|
3
3
|
*
|
|
4
|
-
* When the DB is open but empty (e.g
|
|
5
|
-
* getMilestoneSlices() returns []
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
4
|
+
* When the DB is open but empty (e.g. after crash/truncation),
|
|
5
|
+
* getMilestoneSlices() returns []. The fix in showDiscuss() falls back to
|
|
6
|
+
* parsing slices from the on-disk ROADMAP file instead of declaring "all
|
|
7
|
+
* slices are complete." These tests pin the parser contract that the
|
|
8
|
+
* fallback depends on: incomplete checkboxes (`[ ]`) yield `done=false`
|
|
9
|
+
* slices and completed checkboxes (`[x]`) yield `done=true`.
|
|
10
|
+
*
|
|
11
|
+
* The earlier source-grep / regex-on-showDiscuss-body tests (audit verdicts
|
|
12
|
+
* SOURCE_GREP / POSITIONAL — see #4826/#4829) were dropped; they pinned a
|
|
13
|
+
* specific surface form rather than behaviour.
|
|
9
14
|
*/
|
|
10
15
|
|
|
11
16
|
import { describe, test } from "node:test";
|
|
12
17
|
import assert from "node:assert/strict";
|
|
13
|
-
import { readFileSync } from "node:fs";
|
|
14
|
-
import { fileURLToPath } from "node:url";
|
|
15
|
-
import { dirname, join } from "node:path";
|
|
16
18
|
import { parseRoadmapSlices } from "../roadmap-slices.ts";
|
|
17
19
|
|
|
18
|
-
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
19
|
-
|
|
20
|
-
function readGuidedFlowSource(): string {
|
|
21
|
-
const thisFile = fileURLToPath(import.meta.url);
|
|
22
|
-
const thisDir = dirname(thisFile);
|
|
23
|
-
return readFileSync(join(thisDir, "..", "guided-flow.ts"), "utf-8");
|
|
24
|
-
}
|
|
25
|
-
|
|
26
20
|
const SAMPLE_ROADMAP = `# M012 Roadmap
|
|
27
21
|
|
|
28
22
|
## Slices
|
|
@@ -34,80 +28,23 @@ const SAMPLE_ROADMAP = `# M012 Roadmap
|
|
|
34
28
|
> After this: dashboard renders
|
|
35
29
|
`;
|
|
36
30
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
describe("discuss-empty-db-fallback (#2892)", () => {
|
|
40
|
-
|
|
41
|
-
test("1. parseRoadmapSlices extracts slices from a valid ROADMAP", () => {
|
|
31
|
+
describe("discuss-empty-db-fallback parser contract (#2892)", () => {
|
|
32
|
+
test("parseRoadmapSlices extracts slices from a valid ROADMAP", () => {
|
|
42
33
|
const slices = parseRoadmapSlices(SAMPLE_ROADMAP);
|
|
43
34
|
assert.strictEqual(slices.length, 3, "should parse 3 slices from sample roadmap");
|
|
44
|
-
|
|
45
|
-
assert.
|
|
46
|
-
assert.strictEqual(slices[2]!.id, "S03");
|
|
47
|
-
// All slices are incomplete ([ ] not [x])
|
|
48
|
-
assert.ok(slices.every(s => !s.done), "all slices should be incomplete");
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
test("2. guided-flow imports parseRoadmapSlices for roadmap fallback", () => {
|
|
52
|
-
const source = readGuidedFlowSource();
|
|
53
|
-
assert.ok(
|
|
54
|
-
source.includes("parseRoadmapSlices"),
|
|
55
|
-
"guided-flow must import parseRoadmapSlices to support roadmap fallback when DB is empty",
|
|
56
|
-
);
|
|
35
|
+
const ids = slices.map(s => s.id).sort();
|
|
36
|
+
assert.deepStrictEqual(ids, ["S01", "S02", "S03"]);
|
|
57
37
|
});
|
|
58
38
|
|
|
59
|
-
test("
|
|
60
|
-
const
|
|
61
|
-
// The fix must add a fallback that checks normSlices.length === 0 && roadmapContent
|
|
62
|
-
// and repopulates normSlices from the roadmap before the pendingSlices guard.
|
|
63
|
-
//
|
|
64
|
-
// Pattern: after DB query produces normSlices, if empty + roadmap exists,
|
|
65
|
-
// fall back to parseRoadmapSlices(roadmapContent).
|
|
66
|
-
const fallbackPattern = /normSlices\.length\s*===\s*0\s*&&\s*roadmapContent/;
|
|
39
|
+
test("incomplete checkboxes yield done=false (so fallback shows them as pending)", () => {
|
|
40
|
+
const slices = parseRoadmapSlices(SAMPLE_ROADMAP);
|
|
67
41
|
assert.ok(
|
|
68
|
-
|
|
69
|
-
"
|
|
42
|
+
slices.every(s => s.done === false),
|
|
43
|
+
"all 3 incomplete roadmap slices must be done=false — otherwise the empty-DB fallback would falsely report them complete (#2892)",
|
|
70
44
|
);
|
|
71
45
|
});
|
|
72
46
|
|
|
73
|
-
test("
|
|
74
|
-
const source = readGuidedFlowSource();
|
|
75
|
-
// Extract the showDiscuss function body
|
|
76
|
-
const fnMatch = source.match(
|
|
77
|
-
/async function showDiscuss\s*\([^)]*\)[^{]*\{([\s\S]*?)\nfunction\s/,
|
|
78
|
-
);
|
|
79
|
-
assert.ok(!!fnMatch, "showDiscuss function body must be found");
|
|
80
|
-
|
|
81
|
-
if (fnMatch) {
|
|
82
|
-
const body = fnMatch[1]!;
|
|
83
|
-
// After the DB query block (isDbAvailable/getMilestoneSlices), there should
|
|
84
|
-
// be a roadmap fallback BEFORE the pendingSlices.length === 0 check.
|
|
85
|
-
// Find the getMilestoneSlices call and the pendingSlices === 0 check
|
|
86
|
-
const dbQueryIdx = body.indexOf("getMilestoneSlices");
|
|
87
|
-
const fallbackIdx = body.indexOf("parseRoadmapSlices");
|
|
88
|
-
const pendingGuardIdx = body.indexOf('pendingSlices.length === 0');
|
|
89
|
-
|
|
90
|
-
assert.ok(dbQueryIdx > 0, "getMilestoneSlices call must exist");
|
|
91
|
-
assert.ok(fallbackIdx > 0, "parseRoadmapSlices fallback must exist");
|
|
92
|
-
assert.ok(pendingGuardIdx > 0, "pendingSlices.length === 0 guard must exist");
|
|
93
|
-
assert.ok(
|
|
94
|
-
fallbackIdx > dbQueryIdx && fallbackIdx < pendingGuardIdx,
|
|
95
|
-
"parseRoadmapSlices fallback must appear BETWEEN DB query and pendingSlices === 0 guard",
|
|
96
|
-
);
|
|
97
|
-
}
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
test("5. roadmap-parsed slices map to NormSlice format with done=false by default", () => {
|
|
101
|
-
// When falling back to roadmap, incomplete slices ([ ]) should map to done:false,
|
|
102
|
-
// ensuring they appear as pending and are NOT falsely reported as complete.
|
|
103
|
-
const slices = parseRoadmapSlices(SAMPLE_ROADMAP);
|
|
104
|
-
const normSlices = slices.map(s => ({ id: s.id, done: s.done, title: s.title }));
|
|
105
|
-
const pendingSlices = normSlices.filter(s => !s.done);
|
|
106
|
-
assert.strictEqual(pendingSlices.length, 3,
|
|
107
|
-
"all 3 incomplete roadmap slices should be pending — not falsely treated as complete");
|
|
108
|
-
});
|
|
109
|
-
|
|
110
|
-
test("6. roadmap with completed slices correctly reports them as done", () => {
|
|
47
|
+
test("completed checkboxes yield done=true; mixed roadmap surfaces only the open slices as pending", () => {
|
|
111
48
|
const completedRoadmap = `# M012 Roadmap
|
|
112
49
|
|
|
113
50
|
## Slices
|
|
@@ -119,9 +56,7 @@ describe("discuss-empty-db-fallback (#2892)", () => {
|
|
|
119
56
|
> After this: dashboard renders
|
|
120
57
|
`;
|
|
121
58
|
const slices = parseRoadmapSlices(completedRoadmap);
|
|
122
|
-
const
|
|
123
|
-
|
|
124
|
-
assert.strictEqual(pendingSlices.length, 1, "only S02 should be pending");
|
|
125
|
-
assert.strictEqual(pendingSlices[0]!.id, "S02");
|
|
59
|
+
const pendingIds = slices.filter(s => !s.done).map(s => s.id);
|
|
60
|
+
assert.deepStrictEqual(pendingIds, ["S02"], "only S02 should be reported as pending");
|
|
126
61
|
});
|
|
127
62
|
});
|