@opengsd/gsd-pi 1.3.0-dev.65546769 → 1.3.0-dev.72e3af2a
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/claude-code-cli/stream-adapter.js +11 -2
- package/dist/resources/extensions/google-cli/stream-adapter.js +82 -15
- package/dist/resources/extensions/gsd/artifact-verification.js +427 -0
- package/dist/resources/extensions/gsd/auto/orchestrator.js +12 -3
- package/dist/resources/extensions/gsd/auto/session.js +3 -0
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +28 -1
- package/dist/resources/extensions/gsd/auto-dispatch.js +20 -19
- package/dist/resources/extensions/gsd/auto-prompts.js +26 -11
- package/dist/resources/extensions/gsd/auto-recovery.js +6 -507
- package/dist/resources/extensions/gsd/auto-runtime-state.js +4 -5
- package/dist/resources/extensions/gsd/auto-timeout-recovery.js +3 -3
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +103 -13
- package/dist/resources/extensions/gsd/bootstrap/core-session-tools.js +38 -0
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +6 -1
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +2 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +10 -19
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +46 -19
- package/dist/resources/extensions/gsd/bootstrap/tool-call-loop-guard.js +68 -10
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +1 -1
- package/dist/resources/extensions/gsd/commands-context.js +19 -1
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +16 -10
- package/dist/resources/extensions/gsd/commands-worktree.js +12 -10
- package/dist/resources/extensions/gsd/dashboard-overlay.js +32 -3
- package/dist/resources/extensions/gsd/db/queries.js +60 -0
- package/dist/resources/extensions/gsd/db-workspace.js +55 -3
- package/dist/resources/extensions/gsd/doctor-providers.js +92 -8
- package/dist/resources/extensions/gsd/exec-sandbox.js +45 -9
- package/dist/resources/extensions/gsd/forensics.js +2 -32
- package/dist/resources/extensions/gsd/git-service.js +4 -4
- package/dist/resources/extensions/gsd/guided-flow-queue.js +66 -5
- package/dist/resources/extensions/gsd/health-widget.js +55 -29
- package/dist/resources/extensions/gsd/layout-policy.js +3 -1
- package/dist/resources/extensions/gsd/markdown-renderer.js +8 -9
- package/dist/resources/extensions/gsd/memory-consolidation-scanner.js +44 -21
- package/dist/resources/extensions/gsd/migration-auto-check.js +22 -0
- package/dist/resources/extensions/gsd/milestone-ids.js +32 -2
- package/dist/resources/extensions/gsd/milestone-implementation-evidence.js +26 -20
- package/dist/resources/extensions/gsd/prompts/code-review.md +6 -4
- package/dist/resources/extensions/gsd/quick.js +45 -2
- package/dist/resources/extensions/gsd/session-forensics.js +11 -1
- package/dist/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -1
- package/dist/resources/extensions/gsd/state/derive/cache.js +28 -0
- package/dist/resources/extensions/gsd/state/derive/db-open.js +39 -0
- package/dist/resources/extensions/gsd/state/derive/from-db.js +452 -0
- package/dist/resources/extensions/gsd/state/derive/index.js +75 -0
- package/dist/resources/extensions/gsd/state/derive/interrupted-work.js +21 -0
- package/dist/resources/extensions/gsd/state-reconciliation/drift/stale-render.js +45 -2
- package/dist/resources/extensions/gsd/state-reconciliation/index.js +48 -23
- package/dist/resources/extensions/gsd/state-reconciliation/registry.js +32 -28
- package/dist/resources/extensions/gsd/state.js +12 -611
- package/dist/resources/extensions/gsd/tools/complete-slice.js +2 -2
- package/dist/resources/extensions/gsd/tools/complete-task.js +43 -14
- package/dist/resources/extensions/gsd/tools/exec-tool.js +7 -2
- package/dist/resources/extensions/gsd/unit-context-composer.js +23 -7
- package/dist/resources/extensions/gsd/unit-registry.js +32 -4
- package/dist/resources/extensions/gsd/unmerged-milestone-guard.js +33 -3
- package/dist/resources/extensions/gsd/validation-block-guard.js +9 -4
- package/dist/resources/extensions/gsd/workflow-projections.js +19 -14
- package/dist/resources/extensions/gsd/workspace-git-preflight.js +30 -1
- package/dist/resources/extensions/gsd/worktree-manager.js +44 -2
- 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 +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +9 -9
- 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/visualizer/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 +10 -10
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- 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/static/chunks/{2659.b7b129ee6a769448.js → 2659.58e950899a9bb82f.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/2772.a7c1fcc69a4685ef.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{3616.3c60753b8ffcbd2e.js → 3616.61a2af74bb8833c8.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/{4283.8e446784528ed9dc.js → 4283.d0d9e0a955e441cb.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/{5826.a46ecdd1cfe8dabc.js → 5826.5421d66c72b9f34e.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/{8785.481aa5869991b760.js → 8785.e29b3134cab1d153.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/8937.640dc9c2aaa1dfad.js +10 -0
- package/dist/web/standalone/.next/static/chunks/app/{page-6644fc6ee8ca1247.js → page-72a856634ad14c10.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/webpack-9c401904f87ded16.js +1 -0
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
- package/package.json +1 -1
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/dist/workflow.d.ts +1 -0
- package/packages/contracts/dist/workflow.d.ts.map +1 -1
- package/packages/contracts/dist/workflow.js +2 -0
- package/packages/contracts/dist/workflow.js.map +1 -1
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/dist/agent-session.d.ts +1 -0
- package/packages/gsd-agent-core/dist/agent-session.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/agent-session.js +3 -0
- package/packages/gsd-agent-core/dist/agent-session.js.map +1 -1
- package/packages/gsd-agent-core/dist/extension-ui-snapshot.d.ts +41 -0
- package/packages/gsd-agent-core/dist/extension-ui-snapshot.d.ts.map +1 -0
- package/packages/gsd-agent-core/dist/extension-ui-snapshot.js +62 -0
- package/packages/gsd-agent-core/dist/extension-ui-snapshot.js.map +1 -0
- package/packages/gsd-agent-core/dist/index.d.ts +2 -0
- package/packages/gsd-agent-core/dist/index.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/index.js +2 -0
- package/packages/gsd-agent-core/dist/index.js.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-events.js +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-events.js.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts +1 -0
- package/packages/gsd-agent-core/dist/session/agent-session-host.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-host.js.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts +5 -0
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.js +60 -3
- package/packages/gsd-agent-core/dist/session/agent-session-prompt.js.map +1 -1
- package/packages/gsd-agent-core/dist/transcript-store.d.ts +58 -0
- package/packages/gsd-agent-core/dist/transcript-store.d.ts.map +1 -0
- package/packages/gsd-agent-core/dist/transcript-store.js +132 -0
- package/packages/gsd-agent-core/dist/transcript-store.js.map +1 -0
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts +2 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js +25 -11
- package/packages/gsd-agent-modes/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.d.ts +4 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.js +7 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller-latency.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts +3 -24
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +26 -829
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.d.ts +58 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.js +312 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-handoff-filter.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.d.ts +31 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.js +130 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-pinned-zone.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts +15 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js +258 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-segment-walker.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.d.ts +13 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.js +118 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-tool-rollup.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.d.ts +9 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.js +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-state.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.d.ts +54 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.js +20 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-ui-state.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts +4 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +9 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.d.ts +56 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.js +44 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/streaming-render-state.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.d.ts +5 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.d.ts.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.js +77 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/tui-transcript-tracker.js.map +1 -0
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js +18 -0
- package/packages/gsd-agent-modes/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/README.md +1 -1
- package/packages/mcp-server/dist/server.d.ts +1 -1
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +3 -3
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts +13 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +34 -20
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- 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/README.md +3 -2
- package/packages/pi-coding-agent/dist/core/session-manager-context.d.ts +9 -0
- package/packages/pi-coding-agent/dist/core/session-manager-context.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-context.js +94 -0
- package/packages/pi-coding-agent/dist/core/session-manager-context.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-list.d.ts +8 -0
- package/packages/pi-coding-agent/dist/core/session-manager-list.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-list.js +244 -0
- package/packages/pi-coding-agent/dist/core/session-manager-list.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-migration.d.ts +12 -0
- package/packages/pi-coding-agent/dist/core/session-manager-migration.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-migration.js +84 -0
- package/packages/pi-coding-agent/dist/core/session-manager-migration.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts +135 -0
- package/packages/pi-coding-agent/dist/core/session-manager-types.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager-types.js +2 -0
- package/packages/pi-coding-agent/dist/core/session-manager-types.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts +6 -154
- package/packages/pi-coding-agent/dist/core/session-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/session-manager.js +22 -459
- package/packages/pi-coding-agent/dist/core/session-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme-schema.d.ts +75 -75
- package/packages/pi-coding-agent/dist/theme/theme-schema.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme-schema.js +1 -1
- package/packages/pi-coding-agent/dist/theme/theme-schema.js.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/theme/theme.js +11 -7
- package/packages/pi-coding-agent/dist/theme/theme.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/package.json +2 -2
- package/packages/rpc-client/package.json +2 -2
- package/pkg/dist/theme/theme-schema.d.ts +75 -75
- package/pkg/dist/theme/theme-schema.d.ts.map +1 -1
- package/pkg/dist/theme/theme-schema.js +1 -1
- package/pkg/dist/theme/theme-schema.js.map +1 -1
- package/pkg/dist/theme/theme.d.ts.map +1 -1
- package/pkg/dist/theme/theme.js +11 -7
- package/pkg/dist/theme/theme.js.map +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +20 -2
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +80 -0
- package/src/resources/extensions/google-cli/stream-adapter.ts +106 -19
- package/src/resources/extensions/gsd/artifact-verification.ts +464 -0
- package/src/resources/extensions/gsd/auto/orchestrator.ts +25 -11
- package/src/resources/extensions/gsd/auto/session.ts +5 -0
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +47 -1
- package/src/resources/extensions/gsd/auto-dispatch.ts +21 -23
- package/src/resources/extensions/gsd/auto-prompts.ts +38 -12
- package/src/resources/extensions/gsd/auto-recovery.ts +10 -508
- package/src/resources/extensions/gsd/auto-runtime-state.ts +4 -5
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +3 -2
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +125 -12
- package/src/resources/extensions/gsd/bootstrap/core-session-tools.ts +43 -0
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +6 -1
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +2 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +11 -19
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +52 -18
- package/src/resources/extensions/gsd/bootstrap/tool-call-loop-guard.ts +74 -10
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +1 -1
- package/src/resources/extensions/gsd/commands-context.ts +18 -1
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +14 -9
- package/src/resources/extensions/gsd/commands-worktree.ts +12 -10
- package/src/resources/extensions/gsd/dashboard-overlay.ts +32 -3
- package/src/resources/extensions/gsd/db/queries.ts +79 -0
- package/src/resources/extensions/gsd/db-workspace.ts +61 -3
- package/src/resources/extensions/gsd/doctor-providers.ts +103 -9
- package/src/resources/extensions/gsd/exec-sandbox.ts +49 -9
- package/src/resources/extensions/gsd/forensics.ts +2 -33
- package/src/resources/extensions/gsd/git-service.ts +5 -5
- package/src/resources/extensions/gsd/guided-flow-queue.ts +89 -4
- package/src/resources/extensions/gsd/health-widget.ts +69 -32
- package/src/resources/extensions/gsd/layout-policy.ts +2 -1
- package/src/resources/extensions/gsd/markdown-renderer.ts +8 -11
- package/src/resources/extensions/gsd/memory-consolidation-scanner.ts +51 -19
- package/src/resources/extensions/gsd/migration-auto-check.ts +23 -0
- package/src/resources/extensions/gsd/milestone-ids.ts +31 -2
- package/src/resources/extensions/gsd/milestone-implementation-evidence.ts +35 -21
- package/src/resources/extensions/gsd/prompts/code-review.md +6 -4
- package/src/resources/extensions/gsd/quick.ts +43 -2
- package/src/resources/extensions/gsd/session-forensics.ts +11 -1
- package/src/resources/extensions/gsd/skills/gsd-headless/references/commands.md +1 -1
- package/src/resources/extensions/gsd/state/derive/cache.ts +46 -0
- package/src/resources/extensions/gsd/state/derive/db-open.ts +45 -0
- package/src/resources/extensions/gsd/state/derive/from-db.ts +561 -0
- package/src/resources/extensions/gsd/state/derive/index.ts +104 -0
- package/src/resources/extensions/gsd/state/derive/interrupted-work.ts +31 -0
- package/src/resources/extensions/gsd/state-reconciliation/drift/stale-render.ts +81 -7
- package/src/resources/extensions/gsd/state-reconciliation/index.ts +50 -24
- package/src/resources/extensions/gsd/state-reconciliation/registry.ts +43 -28
- package/src/resources/extensions/gsd/state.ts +32 -732
- package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +98 -1
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +111 -1
- package/src/resources/extensions/gsd/tests/commands-context.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/commands-gsd-core.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/commands-worktree-clean.test.ts +80 -0
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +11 -0
- package/src/resources/extensions/gsd/tests/complete-task-rollback-evidence.test.ts +48 -8
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +75 -0
- package/src/resources/extensions/gsd/tests/dashboard-overlay.test.ts +55 -2
- package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +26 -1
- package/src/resources/extensions/gsd/tests/doctor-forensics-db-open-regression.test.ts +70 -2
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +107 -0
- package/src/resources/extensions/gsd/tests/exec-graceful-kill.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/exec-tool.test.ts +45 -1
- package/src/resources/extensions/gsd/tests/forensics-error-filter.test.ts +88 -0
- package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +268 -3
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +119 -1
- package/src/resources/extensions/gsd/tests/integration/queue-active-milestone-context-budget.test.ts +93 -0
- package/src/resources/extensions/gsd/tests/integration/quick-branch-lifecycle.test.ts +56 -9
- package/src/resources/extensions/gsd/tests/knowledge-cold-start.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/memory-consolidation-scanner.test.ts +78 -0
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +70 -0
- package/src/resources/extensions/gsd/tests/orchestrator-logs.test.ts +43 -1
- package/src/resources/extensions/gsd/tests/parallel-research-dispatch.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/pipeline-variant-dispatch.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +54 -1
- package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +50 -14
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +195 -1
- package/src/resources/extensions/gsd/tests/read-uat-gate-verdict.test.ts +185 -0
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/runtime-invariant-modules.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/stalled-tool-recovery.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +191 -0
- package/src/resources/extensions/gsd/tests/tool-call-loop-guard.test.ts +151 -0
- package/src/resources/extensions/gsd/tests/tool-param-optionality.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +193 -14
- package/src/resources/extensions/gsd/tests/unmerged-milestone-guard.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/validation-block-guard.test.ts +79 -0
- package/src/resources/extensions/gsd/tests/verify-artifact-tightened.test.ts +66 -0
- package/src/resources/extensions/gsd/tests/workspace-git-preflight.test.ts +151 -2
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +39 -0
- package/src/resources/extensions/gsd/tools/complete-slice.ts +2 -2
- package/src/resources/extensions/gsd/tools/complete-task.ts +53 -15
- package/src/resources/extensions/gsd/tools/exec-tool.ts +7 -3
- package/src/resources/extensions/gsd/unit-context-composer.ts +33 -7
- package/src/resources/extensions/gsd/unit-registry.ts +32 -4
- package/src/resources/extensions/gsd/unmerged-milestone-guard.ts +41 -5
- package/src/resources/extensions/gsd/validation-block-guard.ts +13 -7
- package/src/resources/extensions/gsd/workflow-projections.ts +20 -14
- package/src/resources/extensions/gsd/workspace-git-preflight.ts +31 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +41 -1
- package/dist/web/standalone/.next/static/chunks/2772.bfa657f49f955239.js +0 -1
- package/dist/web/standalone/.next/static/chunks/796.e0bdc932325d7e03.js +0 -10
- package/dist/web/standalone/.next/static/chunks/webpack-f46ea08200a0227e.js +0 -1
- /package/dist/web/standalone/.next/static/{BTKtGFF1Y-hvVJEGhBRo9 → O7xDYXO0r4zFhIzY1hrWV}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{BTKtGFF1Y-hvVJEGhBRo9 → O7xDYXO0r4zFhIzY1hrWV}/_ssgManifest.js +0 -0
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
|
|
4
4
|
import test from "node:test";
|
|
5
5
|
import assert from "node:assert/strict";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { execFileSync } from "node:child_process";
|
|
7
|
+
import { chmodSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
8
|
+
import { performance } from "node:perf_hooks";
|
|
9
|
+
import { delimiter, join } from "node:path";
|
|
8
10
|
import { tmpdir } from "node:os";
|
|
9
11
|
import {
|
|
10
12
|
buildHealthLines,
|
|
@@ -12,8 +14,9 @@ import {
|
|
|
12
14
|
formatRelativeTime,
|
|
13
15
|
type HealthWidgetData,
|
|
14
16
|
} from "../health-widget-core.ts";
|
|
15
|
-
import { HEALTH_WIDGET_ACTIVE_HINTS } from "../health-widget.ts";
|
|
17
|
+
import { HEALTH_WIDGET_ACTIVE_HINTS, getCachedProjectState, initHealthWidget } from "../health-widget.ts";
|
|
16
18
|
import { registerHooks } from "../bootstrap/register-hooks.ts";
|
|
19
|
+
import { GIT_NO_PROMPT_ENV } from "../git-constants.ts";
|
|
17
20
|
|
|
18
21
|
function makeTempDir(prefix: string): string {
|
|
19
22
|
const dir = join(
|
|
@@ -32,6 +35,58 @@ function cleanup(dir: string): void {
|
|
|
32
35
|
}
|
|
33
36
|
}
|
|
34
37
|
|
|
38
|
+
function runGit(cwd: string, ...args: string[]): string {
|
|
39
|
+
return execFileSync("git", args, {
|
|
40
|
+
cwd,
|
|
41
|
+
encoding: "utf-8",
|
|
42
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
43
|
+
}).trim();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function makeTempRepo(prefix: string): string {
|
|
47
|
+
const dir = makeTempDir(prefix);
|
|
48
|
+
runGit(dir, "init");
|
|
49
|
+
runGit(dir, "config", "user.email", "test@test.com");
|
|
50
|
+
runGit(dir, "config", "user.name", "Test");
|
|
51
|
+
writeFileSync(join(dir, "README.md"), "# test\n", "utf-8");
|
|
52
|
+
runGit(dir, "add", "README.md");
|
|
53
|
+
runGit(dir, "commit", "-m", "initial commit");
|
|
54
|
+
return dir;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function installSlowGitLogShim(binDir: string): void {
|
|
58
|
+
writeFileSync(
|
|
59
|
+
join(binDir, "git"),
|
|
60
|
+
[
|
|
61
|
+
"#!/bin/sh",
|
|
62
|
+
'if [ "$1" = "log" ]; then sleep 1; fi',
|
|
63
|
+
'PATH="$GSD_REAL_PATH"',
|
|
64
|
+
"export PATH",
|
|
65
|
+
'exec git "$@"',
|
|
66
|
+
"",
|
|
67
|
+
].join("\n"),
|
|
68
|
+
"utf-8",
|
|
69
|
+
);
|
|
70
|
+
chmodSync(join(binDir, "git"), 0o755);
|
|
71
|
+
|
|
72
|
+
writeFileSync(
|
|
73
|
+
join(binDir, "git.cmd"),
|
|
74
|
+
[
|
|
75
|
+
"@echo off",
|
|
76
|
+
'if "%1"=="log" powershell -NoProfile -Command "Start-Sleep -Seconds 1"',
|
|
77
|
+
'set "PATH=%GSD_REAL_PATH%"',
|
|
78
|
+
"git %*",
|
|
79
|
+
"",
|
|
80
|
+
].join("\r\n"),
|
|
81
|
+
"utf-8",
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
type HealthWidgetFactory = (
|
|
86
|
+
tui: { requestRender(): void },
|
|
87
|
+
theme: { fg(style: string, text: string): string },
|
|
88
|
+
) => { dispose(): void };
|
|
89
|
+
|
|
35
90
|
function activeData(overrides: Partial<HealthWidgetData> = {}): HealthWidgetData {
|
|
36
91
|
return {
|
|
37
92
|
projectState: "active",
|
|
@@ -70,6 +125,86 @@ test("detectHealthWidgetProjectState: milestone without metrics returns active",
|
|
|
70
125
|
assert.equal(detectHealthWidgetProjectState(dir), "active");
|
|
71
126
|
});
|
|
72
127
|
|
|
128
|
+
test("getCachedProjectState: reuses project state until the refresh TTL expires", (t) => {
|
|
129
|
+
const dir = makeTempDir("cached-state");
|
|
130
|
+
t.after(() => { cleanup(dir); });
|
|
131
|
+
|
|
132
|
+
let now = 1_000_000;
|
|
133
|
+
const dateNow = t.mock.method(Date, "now", () => now);
|
|
134
|
+
t.after(() => { dateNow.mock.restore(); });
|
|
135
|
+
|
|
136
|
+
mkdirSync(join(dir, ".gsd"), { recursive: true });
|
|
137
|
+
assert.equal(getCachedProjectState(dir), "initialized");
|
|
138
|
+
|
|
139
|
+
mkdirSync(join(dir, ".gsd", "milestones", "M001"), { recursive: true });
|
|
140
|
+
assert.equal(getCachedProjectState(dir), "initialized");
|
|
141
|
+
|
|
142
|
+
now += 60_000;
|
|
143
|
+
assert.equal(getCachedProjectState(dir), "initialized");
|
|
144
|
+
|
|
145
|
+
now += 1;
|
|
146
|
+
assert.equal(getCachedProjectState(dir), "active");
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
test("getCachedProjectState: force=true bypasses TTL and returns fresh state within TTL window", (t) => {
|
|
150
|
+
const dir = makeTempDir("forced-state");
|
|
151
|
+
t.after(() => { cleanup(dir); });
|
|
152
|
+
|
|
153
|
+
let now = 2_000_000;
|
|
154
|
+
const dateNow = t.mock.method(Date, "now", () => now);
|
|
155
|
+
t.after(() => { dateNow.mock.restore(); });
|
|
156
|
+
|
|
157
|
+
mkdirSync(join(dir, ".gsd"), { recursive: true });
|
|
158
|
+
// Prime the cache with "initialized".
|
|
159
|
+
assert.equal(getCachedProjectState(dir), "initialized");
|
|
160
|
+
|
|
161
|
+
// Disk changes within the TTL window.
|
|
162
|
+
mkdirSync(join(dir, ".gsd", "milestones", "M001"), { recursive: true });
|
|
163
|
+
now += 1_000; // well within 60s TTL
|
|
164
|
+
|
|
165
|
+
// Normal call still returns stale cached value.
|
|
166
|
+
assert.equal(getCachedProjectState(dir), "initialized");
|
|
167
|
+
|
|
168
|
+
// force=true bypasses TTL and returns the fresh disk state.
|
|
169
|
+
assert.equal(getCachedProjectState(dir, true), "active");
|
|
170
|
+
|
|
171
|
+
// Subsequent non-forced call also reflects the freshened cache.
|
|
172
|
+
assert.equal(getCachedProjectState(dir), "active");
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
test("initHealthWidget: re-init paints fresh project state within cache TTL", (t) => {
|
|
176
|
+
const dir = makeTempDir("reinit-state");
|
|
177
|
+
t.after(() => { cleanup(dir); });
|
|
178
|
+
|
|
179
|
+
let now = 3_000_000;
|
|
180
|
+
const dateNow = t.mock.method(Date, "now", () => now);
|
|
181
|
+
t.after(() => { dateNow.mock.restore(); });
|
|
182
|
+
|
|
183
|
+
const originalCwd = process.cwd();
|
|
184
|
+
process.chdir(dir);
|
|
185
|
+
t.after(() => { process.chdir(originalCwd); });
|
|
186
|
+
|
|
187
|
+
const initialLineSets: string[][] = [];
|
|
188
|
+
const ctx = {
|
|
189
|
+
hasUI: true,
|
|
190
|
+
ui: {
|
|
191
|
+
setWidget: (_key: string, value: unknown) => {
|
|
192
|
+
if (Array.isArray(value)) initialLineSets.push(value as string[]);
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
} as any;
|
|
196
|
+
|
|
197
|
+
mkdirSync(join(dir, ".gsd"), { recursive: true });
|
|
198
|
+
initHealthWidget(ctx);
|
|
199
|
+
assert.equal(initialLineSets.at(-1)?.[0], " GSD Project Initialized");
|
|
200
|
+
|
|
201
|
+
mkdirSync(join(dir, ".gsd", "milestones", "M001"), { recursive: true });
|
|
202
|
+
now += 1_000;
|
|
203
|
+
|
|
204
|
+
initHealthWidget(ctx);
|
|
205
|
+
assert.match(initialLineSets.at(-1)?.[0] ?? "", /System OK/);
|
|
206
|
+
});
|
|
207
|
+
|
|
73
208
|
test("buildHealthLines: none state shows single onboarding line pointing at /gsd", (t) => {
|
|
74
209
|
const lines = buildHealthLines(activeData({ projectState: "none" }));
|
|
75
210
|
assert.equal(lines.length, 1, "renders exactly one line");
|
|
@@ -101,6 +236,136 @@ test("health widget active hints include visualization and notifications", () =>
|
|
|
101
236
|
assert.match(HEALTH_WIDGET_ACTIVE_HINTS, /\/gsd help/);
|
|
102
237
|
});
|
|
103
238
|
|
|
239
|
+
test("health widget async refresh does not block timers while git log is slow", async (t) => {
|
|
240
|
+
const dir = makeTempRepo("slow-git-log");
|
|
241
|
+
const binDir = makeTempDir("slow-git-log-bin");
|
|
242
|
+
mkdirSync(join(dir, ".gsd", "milestones", "M001"), { recursive: true });
|
|
243
|
+
installSlowGitLogShim(binDir);
|
|
244
|
+
|
|
245
|
+
const originalCwd = process.cwd();
|
|
246
|
+
const originalProcessPath = process.env.PATH;
|
|
247
|
+
const originalEnvPath = GIT_NO_PROMPT_ENV.PATH;
|
|
248
|
+
const originalEnvRealPath = GIT_NO_PROMPT_ENV.GSD_REAL_PATH;
|
|
249
|
+
const shimmedPath = `${binDir}${delimiter}${originalProcessPath ?? ""}`;
|
|
250
|
+
|
|
251
|
+
process.chdir(dir);
|
|
252
|
+
process.env.PATH = shimmedPath;
|
|
253
|
+
GIT_NO_PROMPT_ENV.PATH = shimmedPath;
|
|
254
|
+
GIT_NO_PROMPT_ENV.GSD_REAL_PATH = originalProcessPath ?? "";
|
|
255
|
+
|
|
256
|
+
let factory: HealthWidgetFactory | null = null;
|
|
257
|
+
let resolveRefresh: (() => void) | undefined;
|
|
258
|
+
const refreshed = new Promise<void>((resolve) => { resolveRefresh = resolve; });
|
|
259
|
+
const gaps: number[] = [];
|
|
260
|
+
let lastTick = performance.now();
|
|
261
|
+
let heartbeat: NodeJS.Timeout | undefined;
|
|
262
|
+
let refreshTimeout: NodeJS.Timeout | undefined;
|
|
263
|
+
let widget: { dispose(): void } | undefined;
|
|
264
|
+
|
|
265
|
+
t.after(() => {
|
|
266
|
+
if (widget) widget.dispose();
|
|
267
|
+
if (heartbeat) clearInterval(heartbeat);
|
|
268
|
+
if (refreshTimeout) clearTimeout(refreshTimeout);
|
|
269
|
+
process.chdir(originalCwd);
|
|
270
|
+
if (originalProcessPath === undefined) delete process.env.PATH;
|
|
271
|
+
else process.env.PATH = originalProcessPath;
|
|
272
|
+
if (originalEnvPath === undefined) delete GIT_NO_PROMPT_ENV.PATH;
|
|
273
|
+
else GIT_NO_PROMPT_ENV.PATH = originalEnvPath;
|
|
274
|
+
if (originalEnvRealPath === undefined) delete GIT_NO_PROMPT_ENV.GSD_REAL_PATH;
|
|
275
|
+
else GIT_NO_PROMPT_ENV.GSD_REAL_PATH = originalEnvRealPath;
|
|
276
|
+
cleanup(binDir);
|
|
277
|
+
cleanup(dir);
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
initHealthWidget({
|
|
281
|
+
hasUI: true,
|
|
282
|
+
ui: {
|
|
283
|
+
setWidget: (_key: string, value: unknown) => {
|
|
284
|
+
if (typeof value === "function") factory = value as HealthWidgetFactory;
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
} as any);
|
|
288
|
+
|
|
289
|
+
assert.ok(factory, "health widget factory is registered");
|
|
290
|
+
|
|
291
|
+
heartbeat = setInterval(() => {
|
|
292
|
+
const now = performance.now();
|
|
293
|
+
gaps.push(now - lastTick);
|
|
294
|
+
lastTick = now;
|
|
295
|
+
}, 25);
|
|
296
|
+
|
|
297
|
+
// assert.ok above guards at runtime; double-cast is needed because TypeScript
|
|
298
|
+
// cannot track the factory assignment through the `as any` closure call.
|
|
299
|
+
widget = (factory as unknown as HealthWidgetFactory)(
|
|
300
|
+
{ requestRender: () => { resolveRefresh?.(); } },
|
|
301
|
+
{ fg: (_style: string, text: string) => text },
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
await Promise.race([
|
|
305
|
+
refreshed,
|
|
306
|
+
new Promise<never>((_, reject) => {
|
|
307
|
+
refreshTimeout = setTimeout(() => reject(new Error("health widget refresh did not complete")), 4_000);
|
|
308
|
+
}),
|
|
309
|
+
]);
|
|
310
|
+
if (refreshTimeout) clearTimeout(refreshTimeout);
|
|
311
|
+
|
|
312
|
+
assert.ok(gaps.length > 0, "heartbeat ran while refresh was in flight");
|
|
313
|
+
const maxGap = Math.max(...gaps);
|
|
314
|
+
assert.ok(maxGap < 750, `slow git log must not starve timers; max gap was ${Math.round(maxGap)}ms`);
|
|
315
|
+
});
|
|
316
|
+
|
|
317
|
+
test("initHealthWidget: synchronous first-paint render never contains last-commit info (regression #964)", (t) => {
|
|
318
|
+
// Before the fix, loadHealthWidgetData with includeChecks:true called
|
|
319
|
+
// loadLastCommitInfo — synchronous native-git-bridge ops (nativeIsRepo,
|
|
320
|
+
// nativeGetCurrentBranch, nativeLastCommitEpoch, nativeCommitSubject) — which
|
|
321
|
+
// froze the TUI on slow repos. The fix removes that synchronous git path
|
|
322
|
+
// entirely: lastCommitEpoch/lastCommitMessage are now always null from the
|
|
323
|
+
// synchronous loader; only the async refresh (loadLastCommitInfoAsync) fills
|
|
324
|
+
// them in. This test guards that contract by verifying that the initial
|
|
325
|
+
// string-array setWidget call never contains "Last commit:" even on a real
|
|
326
|
+
// git repo where native git queries would succeed.
|
|
327
|
+
const dir = makeTempRepo("sync-last-commit-regression");
|
|
328
|
+
mkdirSync(join(dir, ".gsd", "milestones", "M001"), { recursive: true });
|
|
329
|
+
|
|
330
|
+
const originalCwd = process.cwd();
|
|
331
|
+
process.chdir(dir);
|
|
332
|
+
|
|
333
|
+
let widget: { dispose(): void } | undefined;
|
|
334
|
+
t.after(() => {
|
|
335
|
+
if (widget) widget.dispose();
|
|
336
|
+
process.chdir(originalCwd);
|
|
337
|
+
cleanup(dir);
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
const initialRenders: string[][] = [];
|
|
341
|
+
|
|
342
|
+
initHealthWidget({
|
|
343
|
+
hasUI: true,
|
|
344
|
+
ui: {
|
|
345
|
+
setWidget: (_key: string, value: unknown) => {
|
|
346
|
+
if (Array.isArray(value)) {
|
|
347
|
+
initialRenders.push(value as string[]);
|
|
348
|
+
} else if (typeof value === "function") {
|
|
349
|
+
// Instantiate the factory to satisfy dispose(), but do not await the
|
|
350
|
+
// async refresh — we are only inspecting the synchronous first-paint.
|
|
351
|
+
widget = (value as unknown as HealthWidgetFactory)(
|
|
352
|
+
{ requestRender: () => {} },
|
|
353
|
+
{ fg: (_style: string, text: string) => text },
|
|
354
|
+
);
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
},
|
|
358
|
+
} as any);
|
|
359
|
+
|
|
360
|
+
assert.ok(initialRenders.length > 0, "at least one synchronous setWidget call");
|
|
361
|
+
|
|
362
|
+
const combined = initialRenders.flat().join("\n");
|
|
363
|
+
assert.ok(
|
|
364
|
+
!combined.includes("Last commit:"),
|
|
365
|
+
"synchronous first-paint render must not contain 'Last commit:' — sync git path removed (regression #964)",
|
|
366
|
+
);
|
|
367
|
+
});
|
|
368
|
+
|
|
104
369
|
test("buildHealthLines: active state with budget ceiling shows percent summary", (t) => {
|
|
105
370
|
const lines = buildHealthLines(activeData({ budgetSpent: 2.5, budgetCeiling: 10 }));
|
|
106
371
|
assert.equal(lines.length, 1);
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { describe, test } from 'node:test';
|
|
4
4
|
import assert from 'node:assert/strict';
|
|
5
5
|
import { mkdtempSync, mkdirSync, writeFileSync, rmSync, existsSync, symlinkSync, readFileSync, chmodSync } from "node:fs";
|
|
6
|
-
import { join, dirname } from "node:path";
|
|
6
|
+
import { join, dirname, delimiter } from "node:path";
|
|
7
7
|
import { tmpdir } from "node:os";
|
|
8
8
|
import { execFileSync, execSync } from "node:child_process";
|
|
9
9
|
|
|
@@ -23,6 +23,7 @@ import {
|
|
|
23
23
|
type PreMergeCheckResult,
|
|
24
24
|
type TaskCommitContext,
|
|
25
25
|
} from "../../git-service.ts";
|
|
26
|
+
import { GIT_NO_PROMPT_ENV } from "../../git-constants.ts";
|
|
26
27
|
import { nativeAddAllWithExclusions, nativeHasChanges, _resetHasChangesCache } from "../../native-git-bridge.ts";
|
|
27
28
|
function run(command: string, cwd: string): string {
|
|
28
29
|
return execSync(command, { cwd, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
|
|
@@ -405,6 +406,49 @@ describe('git-service', async () => {
|
|
|
405
406
|
}).trim();
|
|
406
407
|
}
|
|
407
408
|
|
|
409
|
+
function findGitExecutable(): string {
|
|
410
|
+
const command = process.platform === "win32" ? "where git" : "command -v git";
|
|
411
|
+
const output = execSync(command, { encoding: "utf-8" });
|
|
412
|
+
const found = output.split(/\r?\n/).map(line => line.trim()).find(Boolean);
|
|
413
|
+
assert.ok(found, "git executable is available");
|
|
414
|
+
return found;
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
function installGitRmCachedCounterShim(shimDir: string): void {
|
|
418
|
+
const shimScript = join(shimDir, "git-shim.cjs");
|
|
419
|
+
writeFileSync(shimScript, `
|
|
420
|
+
const { appendFileSync } = require("node:fs");
|
|
421
|
+
const { spawnSync } = require("node:child_process");
|
|
422
|
+
|
|
423
|
+
const args = process.argv.slice(2);
|
|
424
|
+
if (args[0] === "rm" && args[1] === "--cached") {
|
|
425
|
+
appendFileSync(process.env.GSD_GIT_SHIM_LOG, args.join("\\0") + "\\n");
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
const result = spawnSync(process.env.GSD_REAL_GIT || "git", args, {
|
|
429
|
+
stdio: "inherit",
|
|
430
|
+
env: process.env,
|
|
431
|
+
});
|
|
432
|
+
if (result.error) {
|
|
433
|
+
console.error(result.error.message);
|
|
434
|
+
process.exit(1);
|
|
435
|
+
}
|
|
436
|
+
process.exit(result.status ?? 0);
|
|
437
|
+
`.trimStart(), "utf-8");
|
|
438
|
+
|
|
439
|
+
const posixShim = join(shimDir, "git");
|
|
440
|
+
const quotedScript = `'${shimScript.replace(/'/g, "'\\''")}'`;
|
|
441
|
+
writeFileSync(posixShim, `#!/bin/sh\nexec node ${quotedScript} "$@"\n`, "utf-8");
|
|
442
|
+
chmodSync(posixShim, 0o755);
|
|
443
|
+
|
|
444
|
+
writeFileSync(join(shimDir, "git.cmd"), "@echo off\r\nnode \"%~dp0git-shim.cjs\" %*\r\n", "utf-8");
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function countRmCachedInvocations(logFile: string): number {
|
|
448
|
+
if (!existsSync(logFile)) return 0;
|
|
449
|
+
return readFileSync(logFile, "utf-8").split(/\r?\n/).filter(Boolean).length;
|
|
450
|
+
}
|
|
451
|
+
|
|
408
452
|
// ─── GitServiceImpl: smart staging ─────────────────────────────────────
|
|
409
453
|
|
|
410
454
|
test('GitServiceImpl: smart staging', () => {
|
|
@@ -447,6 +491,80 @@ describe('git-service', async () => {
|
|
|
447
491
|
rmSync(repo, { recursive: true, force: true });
|
|
448
492
|
});
|
|
449
493
|
|
|
494
|
+
test('GitServiceImpl: runtime cleanup runs once per repo across service instances', () => {
|
|
495
|
+
const repo = initTempRepo();
|
|
496
|
+
const shimDir = mkdtempSync(join(tmpdir(), "gsd-git-rm-shim-"));
|
|
497
|
+
const logFile = join(shimDir, "git-rm-cached.log");
|
|
498
|
+
const originalPath = process.env.PATH;
|
|
499
|
+
const originalRealGit = process.env.GSD_REAL_GIT;
|
|
500
|
+
const originalShimLog = process.env.GSD_GIT_SHIM_LOG;
|
|
501
|
+
const originalSafePath = GIT_NO_PROMPT_ENV.PATH;
|
|
502
|
+
const originalSafeRealGit = GIT_NO_PROMPT_ENV.GSD_REAL_GIT;
|
|
503
|
+
const originalSafeShimLog = GIT_NO_PROMPT_ENV.GSD_GIT_SHIM_LOG;
|
|
504
|
+
|
|
505
|
+
try {
|
|
506
|
+
installGitRmCachedCounterShim(shimDir);
|
|
507
|
+
const realGit = findGitExecutable();
|
|
508
|
+
process.env.GSD_REAL_GIT = realGit;
|
|
509
|
+
process.env.GSD_GIT_SHIM_LOG = logFile;
|
|
510
|
+
process.env.PATH = `${shimDir}${delimiter}${originalPath ?? ""}`;
|
|
511
|
+
GIT_NO_PROMPT_ENV.GSD_REAL_GIT = realGit;
|
|
512
|
+
GIT_NO_PROMPT_ENV.GSD_GIT_SHIM_LOG = logFile;
|
|
513
|
+
GIT_NO_PROMPT_ENV.PATH = process.env.PATH;
|
|
514
|
+
|
|
515
|
+
createFile(repo, "src/first.ts", "first");
|
|
516
|
+
const first = new GitServiceImpl(repo).commit({ message: "test: first cleanup pass" });
|
|
517
|
+
assert.deepStrictEqual(first, "test: first cleanup pass", "first commit succeeds");
|
|
518
|
+
assert.deepStrictEqual(
|
|
519
|
+
countRmCachedInvocations(logFile),
|
|
520
|
+
RUNTIME_EXCLUSION_PATHS.length,
|
|
521
|
+
"first service instance checks each runtime exclusion once",
|
|
522
|
+
);
|
|
523
|
+
|
|
524
|
+
createFile(repo, "src/second.ts", "second");
|
|
525
|
+
const second = new GitServiceImpl(repo).commit({ message: "test: second cleanup pass" });
|
|
526
|
+
assert.deepStrictEqual(second, "test: second cleanup pass", "second commit succeeds");
|
|
527
|
+
assert.deepStrictEqual(
|
|
528
|
+
countRmCachedInvocations(logFile),
|
|
529
|
+
RUNTIME_EXCLUSION_PATHS.length,
|
|
530
|
+
"fresh service instance for the same repo does not rerun runtime cleanup",
|
|
531
|
+
);
|
|
532
|
+
} finally {
|
|
533
|
+
if (originalPath === undefined) {
|
|
534
|
+
delete process.env.PATH;
|
|
535
|
+
} else {
|
|
536
|
+
process.env.PATH = originalPath;
|
|
537
|
+
}
|
|
538
|
+
if (originalRealGit === undefined) {
|
|
539
|
+
delete process.env.GSD_REAL_GIT;
|
|
540
|
+
} else {
|
|
541
|
+
process.env.GSD_REAL_GIT = originalRealGit;
|
|
542
|
+
}
|
|
543
|
+
if (originalShimLog === undefined) {
|
|
544
|
+
delete process.env.GSD_GIT_SHIM_LOG;
|
|
545
|
+
} else {
|
|
546
|
+
process.env.GSD_GIT_SHIM_LOG = originalShimLog;
|
|
547
|
+
}
|
|
548
|
+
if (originalSafePath === undefined) {
|
|
549
|
+
delete GIT_NO_PROMPT_ENV.PATH;
|
|
550
|
+
} else {
|
|
551
|
+
GIT_NO_PROMPT_ENV.PATH = originalSafePath;
|
|
552
|
+
}
|
|
553
|
+
if (originalSafeRealGit === undefined) {
|
|
554
|
+
delete GIT_NO_PROMPT_ENV.GSD_REAL_GIT;
|
|
555
|
+
} else {
|
|
556
|
+
GIT_NO_PROMPT_ENV.GSD_REAL_GIT = originalSafeRealGit;
|
|
557
|
+
}
|
|
558
|
+
if (originalSafeShimLog === undefined) {
|
|
559
|
+
delete GIT_NO_PROMPT_ENV.GSD_GIT_SHIM_LOG;
|
|
560
|
+
} else {
|
|
561
|
+
GIT_NO_PROMPT_ENV.GSD_GIT_SHIM_LOG = originalSafeShimLog;
|
|
562
|
+
}
|
|
563
|
+
rmSync(repo, { recursive: true, force: true });
|
|
564
|
+
rmSync(shimDir, { recursive: true, force: true });
|
|
565
|
+
}
|
|
566
|
+
});
|
|
567
|
+
|
|
450
568
|
test('GitServiceImpl: task autoCommit skips keyFiles inside submodules', () => {
|
|
451
569
|
const repo = initTempRepo();
|
|
452
570
|
const subSrc = mkdtempSync(join(tmpdir(), "gsd-git-submodule-src-"));
|
package/src/resources/extensions/gsd/tests/integration/queue-active-milestone-context-budget.test.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { describe, test } from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { mkdtempSync, mkdirSync, rmSync, writeFileSync } from "node:fs";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { tmpdir } from "node:os";
|
|
6
|
+
|
|
7
|
+
import { buildExistingMilestonesContext } from "../../guided-flow-queue.ts";
|
|
8
|
+
import type { GSDState, MilestoneRegistryEntry } from "../../types.ts";
|
|
9
|
+
|
|
10
|
+
const LARGE_BODY = "A".repeat(150_000);
|
|
11
|
+
const LARGE_DRAFT = "D".repeat(150_000);
|
|
12
|
+
const LARGE_ROADMAP = "R".repeat(150_000);
|
|
13
|
+
|
|
14
|
+
function makeState(registry: MilestoneRegistryEntry[]): GSDState {
|
|
15
|
+
return {
|
|
16
|
+
activeMilestone: registry.find(m => m.status === "active") ?? null,
|
|
17
|
+
activeSlice: null,
|
|
18
|
+
activeTask: null,
|
|
19
|
+
phase: "executing",
|
|
20
|
+
recentDecisions: [],
|
|
21
|
+
blockers: [],
|
|
22
|
+
nextAction: "",
|
|
23
|
+
registry,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function writeMilestoneArtifact(base: string, mid: string, suffix: string, content: string): void {
|
|
28
|
+
mkdirSync(join(base, ".gsd", "milestones", mid), { recursive: true });
|
|
29
|
+
writeFileSync(join(base, ".gsd", "milestones", mid, `${mid}-${suffix}.md`), content);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
describe("queue active/pending milestone context budget", () => {
|
|
33
|
+
test("summarizes active and pending artifacts with source paths and bounded excerpts", async () => {
|
|
34
|
+
const tmpBase = mkdtempSync(join(tmpdir(), "gsd-queue-active-budget-"));
|
|
35
|
+
try {
|
|
36
|
+
writeMilestoneArtifact(tmpBase, "M001", "CONTEXT", `# Active context\n\n${LARGE_BODY}\nEND_ACTIVE_CONTEXT`);
|
|
37
|
+
writeMilestoneArtifact(tmpBase, "M001", "ROADMAP", `# Active roadmap\n\n${LARGE_ROADMAP}\nEND_ACTIVE_ROADMAP`);
|
|
38
|
+
writeMilestoneArtifact(tmpBase, "M002", "CONTEXT-DRAFT", `# Pending draft\n\n${LARGE_DRAFT}\nEND_PENDING_DRAFT`);
|
|
39
|
+
writeMilestoneArtifact(tmpBase, "M002", "ROADMAP", `# Pending roadmap\n\n${LARGE_ROADMAP}\nEND_PENDING_ROADMAP`);
|
|
40
|
+
|
|
41
|
+
const registry: MilestoneRegistryEntry[] = [
|
|
42
|
+
{ id: "M001", title: "Active milestone", status: "active" },
|
|
43
|
+
{ id: "M002", title: "Pending milestone", status: "pending" },
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
const context = await buildExistingMilestonesContext(tmpBase, ["M001", "M002"], makeState(registry));
|
|
47
|
+
|
|
48
|
+
assert.match(context, /Source: `.gsd\/milestones\/M001\/M001-CONTEXT.md`/);
|
|
49
|
+
assert.match(context, /Source: `.gsd\/milestones\/M001\/M001-ROADMAP.md`/);
|
|
50
|
+
assert.match(context, /Source: `.gsd\/milestones\/M002\/M002-CONTEXT-DRAFT.md`/);
|
|
51
|
+
assert.match(context, /Source: `.gsd\/milestones\/M002\/M002-ROADMAP.md`/);
|
|
52
|
+
assert.match(context, /Read `.gsd\/milestones\/M001\/M001-CONTEXT.md` for full content/);
|
|
53
|
+
assert.equal(context.includes("END_ACTIVE_CONTEXT"), false);
|
|
54
|
+
assert.equal(context.includes("END_ACTIVE_ROADMAP"), false);
|
|
55
|
+
assert.equal(context.includes("END_PENDING_DRAFT"), false);
|
|
56
|
+
assert.equal(context.includes("END_PENDING_ROADMAP"), false);
|
|
57
|
+
} finally {
|
|
58
|
+
rmSync(tmpBase, { recursive: true, force: true });
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("caps the total existing milestones context", async () => {
|
|
63
|
+
const tmpBase = mkdtempSync(join(tmpdir(), "gsd-queue-total-budget-"));
|
|
64
|
+
try {
|
|
65
|
+
const registry: MilestoneRegistryEntry[] = [];
|
|
66
|
+
const milestoneIds: string[] = [];
|
|
67
|
+
for (let i = 1; i <= 5; i++) {
|
|
68
|
+
const mid = `M${String(i).padStart(3, "0")}`;
|
|
69
|
+
milestoneIds.push(mid);
|
|
70
|
+
registry.push({ id: mid, title: `Pending milestone ${i}`, status: "pending" });
|
|
71
|
+
writeMilestoneArtifact(tmpBase, mid, "CONTEXT", `# ${mid} context\n\n${LARGE_BODY}\nEND_${mid}_CONTEXT`);
|
|
72
|
+
writeMilestoneArtifact(tmpBase, mid, "ROADMAP", `# ${mid} roadmap\n\n${LARGE_ROADMAP}\nEND_${mid}_ROADMAP`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const context = await buildExistingMilestonesContext(tmpBase, milestoneIds, makeState(registry));
|
|
76
|
+
|
|
77
|
+
assert.ok(
|
|
78
|
+
context.length <= 120_000,
|
|
79
|
+
`expected total context to stay within budget, got ${context.length} chars`,
|
|
80
|
+
);
|
|
81
|
+
assert.match(context, /Existing milestones context truncated/);
|
|
82
|
+
for (let i = 1; i <= 5; i++) {
|
|
83
|
+
const mid = `M${String(i).padStart(3, "0")}`;
|
|
84
|
+
assert.match(context, new RegExp(`### ${mid}: Pending milestone ${i}`));
|
|
85
|
+
assert.match(context, new RegExp(`Source: \`.gsd/milestones/${mid}/${mid}-CONTEXT.md\``));
|
|
86
|
+
assert.match(context, new RegExp(`Source: \`.gsd/milestones/${mid}/${mid}-ROADMAP.md\``));
|
|
87
|
+
}
|
|
88
|
+
assert.equal(context.includes("END_M005_ROADMAP"), false);
|
|
89
|
+
} finally {
|
|
90
|
+
rmSync(tmpBase, { recursive: true, force: true });
|
|
91
|
+
}
|
|
92
|
+
});
|
|
93
|
+
});
|
|
@@ -16,6 +16,7 @@ import { execSync } from "node:child_process";
|
|
|
16
16
|
|
|
17
17
|
import { captureIntegrationBranch, getCurrentBranch } from "../../worktree.ts";
|
|
18
18
|
import { readIntegrationBranch, QUICK_BRANCH_RE } from "../../git-service.ts";
|
|
19
|
+
import { disableDebug, enableDebug, getDebugCounters } from "../../debug-logger.ts";
|
|
19
20
|
|
|
20
21
|
function run(command: string, cwd: string): string {
|
|
21
22
|
return execSync(command, { cwd, stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }).trim();
|
|
@@ -205,16 +206,25 @@ test('cleanupQuickBranch: recovers from disk state (cross-session)', async () =>
|
|
|
205
206
|
test('cleanupQuickBranch: no-op without pending state', async () => {
|
|
206
207
|
const repo = createTestRepo();
|
|
207
208
|
const origCwd = process.cwd();
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
const result = cleanupQuickBranch();
|
|
212
|
-
|
|
213
|
-
assert.ok(!result, "returns false when no pending state");
|
|
214
|
-
assert.deepStrictEqual(getCurrentBranch(repo), "main", "stays on main");
|
|
209
|
+
try {
|
|
210
|
+
process.chdir(repo);
|
|
211
|
+
enableDebug(repo);
|
|
215
212
|
|
|
216
|
-
|
|
217
|
-
|
|
213
|
+
const { cleanupQuickBranch } = await import("../../quick.ts");
|
|
214
|
+
const result = cleanupQuickBranch();
|
|
215
|
+
const firstGitInvocations = getDebugCounters().gitInvocations;
|
|
216
|
+
const secondResult = cleanupQuickBranch();
|
|
217
|
+
|
|
218
|
+
assert.ok(!result, "returns false when no pending state");
|
|
219
|
+
assert.ok(!secondResult, "still returns false when no pending state");
|
|
220
|
+
assert.deepStrictEqual(getDebugCounters().gitInvocations, firstGitInvocations,
|
|
221
|
+
"cached no-state cleanup does not re-run git branch inference");
|
|
222
|
+
assert.deepStrictEqual(getCurrentBranch(repo), "main", "stays on main");
|
|
223
|
+
} finally {
|
|
224
|
+
disableDebug();
|
|
225
|
+
process.chdir(origCwd);
|
|
226
|
+
rmSync(repo, { recursive: true, force: true });
|
|
227
|
+
}
|
|
218
228
|
});
|
|
219
229
|
|
|
220
230
|
test('cleanupQuickBranch: infers return state from current gsd/quick branch', async () => {
|
|
@@ -240,6 +250,43 @@ test('cleanupQuickBranch: infers return state from current gsd/quick branch', as
|
|
|
240
250
|
}
|
|
241
251
|
});
|
|
242
252
|
|
|
253
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
254
|
+
// cleanupQuickBranch: stale miss invalidated after mid-session branch switch
|
|
255
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
256
|
+
test('cleanupQuickBranch: clears stale miss when branch switches to gsd/quick mid-session', async () => {
|
|
257
|
+
const repo = createTestRepo();
|
|
258
|
+
const origCwd = process.cwd();
|
|
259
|
+
try {
|
|
260
|
+
// Create a quick branch with real product work (so inference finds a diff)
|
|
261
|
+
run("git checkout -b gsd/quick/3-stale-miss", repo);
|
|
262
|
+
writeFileSync(join(repo, "stale.txt"), "stale miss test\n");
|
|
263
|
+
run("git add stale.txt", repo);
|
|
264
|
+
run('git commit -m "test: stale miss"', repo);
|
|
265
|
+
// Return to main so the first cleanupQuickBranch call records a miss
|
|
266
|
+
run("git checkout main", repo);
|
|
267
|
+
|
|
268
|
+
process.chdir(repo);
|
|
269
|
+
const { cleanupQuickBranch } = await import("../../quick.ts");
|
|
270
|
+
|
|
271
|
+
// First call: on main with no disk state → miss recorded (keyed to "main")
|
|
272
|
+
const result1 = cleanupQuickBranch();
|
|
273
|
+
assert.ok(!result1, "first call (on main) returns false — miss recorded");
|
|
274
|
+
|
|
275
|
+
// Simulate mid-session external branch switch to the stranded quick branch
|
|
276
|
+
run("git checkout gsd/quick/3-stale-miss", repo);
|
|
277
|
+
|
|
278
|
+
// Second call: branch changed from the recorded miss, so cache is invalidated
|
|
279
|
+
// and inferQuickReturnFromBranch runs — cleanup must succeed
|
|
280
|
+
const result2 = cleanupQuickBranch();
|
|
281
|
+
assert.ok(result2, "second call returns true after mid-session switch to quick branch");
|
|
282
|
+
assert.deepStrictEqual(getCurrentBranch(repo), "main",
|
|
283
|
+
"cleanup merged back to main after stale-miss invalidation");
|
|
284
|
+
} finally {
|
|
285
|
+
process.chdir(origCwd);
|
|
286
|
+
rmSync(repo, { recursive: true, force: true });
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
|
|
243
290
|
// ═══════════════════════════════════════════════════════════════════════
|
|
244
291
|
// End-to-end: quick branch does NOT contaminate integration branch
|
|
245
292
|
// ═══════════════════════════════════════════════════════════════════════
|
|
@@ -136,8 +136,10 @@ test("#896 startup maintenance skips repeated sentinel work in one session", asy
|
|
|
136
136
|
closeDatabase();
|
|
137
137
|
assert.equal(isDbAvailable(), false);
|
|
138
138
|
|
|
139
|
+
let sessionId = "session-one";
|
|
139
140
|
const ctx = {
|
|
140
141
|
projectRoot: base,
|
|
142
|
+
sessionManager: { getSessionId: () => sessionId },
|
|
141
143
|
ui: { notify: () => undefined },
|
|
142
144
|
} as unknown as ExtensionContext;
|
|
143
145
|
|
|
@@ -190,6 +192,18 @@ test("#896 startup maintenance skips repeated sentinel work in one session", asy
|
|
|
190
192
|
.prepare("SELECT COUNT(*) AS count FROM memories WHERE structured_fields LIKE '%\"sourceKnowledgeId\":\"P002\"%'")
|
|
191
193
|
.get() as { count: number };
|
|
192
194
|
assert.equal(secondPass.count, 0, "second startup in same session should not re-run KNOWLEDGE.md sentinel backfill");
|
|
195
|
+
|
|
196
|
+
sessionId = "session-two";
|
|
197
|
+
await buildBeforeAgentStartResult(
|
|
198
|
+
{ prompt: "Inspect project knowledge in a new session", systemPrompt: "base system prompt" },
|
|
199
|
+
ctx,
|
|
200
|
+
);
|
|
201
|
+
await _flushDeferredContextMaintenanceForTest(base);
|
|
202
|
+
|
|
203
|
+
const nextSessionPass = adapter
|
|
204
|
+
.prepare("SELECT COUNT(*) AS count FROM memories WHERE structured_fields LIKE '%\"sourceKnowledgeId\":\"P002\"%'")
|
|
205
|
+
.get() as { count: number };
|
|
206
|
+
assert.equal(nextSessionPass.count, 1, "startup maintenance should run once for a later session");
|
|
193
207
|
});
|
|
194
208
|
|
|
195
209
|
test("#896 later turns reopen the project DB after startup maintenance is complete", async (t) => {
|