gsd-pi 2.80.0-dev.c5f2443b3 → 2.80.0-dev.f55d16d13
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/GSD-WORKFLOW.md +2 -2
- package/dist/resources/extensions/github-sync/templates.js +39 -8
- package/dist/resources/extensions/gsd/auto/loop.js +16 -9
- package/dist/resources/extensions/gsd/auto/phases.js +37 -30
- package/dist/resources/extensions/gsd/auto/run-unit.js +19 -15
- package/dist/resources/extensions/gsd/auto-dashboard.js +51 -15
- package/dist/resources/extensions/gsd/auto-dispatch.js +10 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +10 -10
- package/dist/resources/extensions/gsd/auto-prompts.js +111 -1
- package/dist/resources/extensions/gsd/auto-recovery.js +154 -8
- package/dist/resources/extensions/gsd/auto-start.js +2 -3
- package/dist/resources/extensions/gsd/auto.js +9 -1
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +15 -1
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +11 -1
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +129 -1
- package/dist/resources/extensions/gsd/clean-root-preflight.js +42 -4
- package/dist/resources/extensions/gsd/commands-extract-learnings.js +17 -12
- package/dist/resources/extensions/gsd/custom-workflow-engine.js +22 -2
- package/dist/resources/extensions/gsd/db-base-schema.js +14 -0
- package/dist/resources/extensions/gsd/db-migration-steps.js +16 -0
- package/dist/resources/extensions/gsd/detection.js +106 -0
- package/dist/resources/extensions/gsd/graph.js +9 -3
- package/dist/resources/extensions/gsd/gsd-db.js +102 -2
- package/dist/resources/extensions/gsd/guided-flow.js +2 -2
- package/dist/resources/extensions/gsd/pr-evidence.js +57 -16
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +7 -8
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
- package/dist/resources/extensions/gsd/safety/evidence-collector.js +10 -2
- package/dist/resources/extensions/gsd/working-output-messages.js +64 -0
- package/dist/resources/extensions/gsd/worktree-manager.js +16 -14
- 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 +11 -11
- 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 +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.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/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +11 -11
- package/dist/web/standalone/.next/server/chunks/6897.js +3 -3
- 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/{8336.6f6f30e410419aff.js → 8336.631939fb583761fa.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/{webpack-d82dbee6356c1733.js → webpack-0481f1221120a7c6.js} +1 -1
- package/package.json +10 -6
- package/packages/contracts/package.json +1 -1
- package/packages/pi-ai/dist/models/fake-model.d.ts +12 -0
- package/packages/pi-ai/dist/models/fake-model.d.ts.map +1 -0
- package/packages/pi-ai/dist/models/fake-model.js +27 -0
- package/packages/pi-ai/dist/models/fake-model.js.map +1 -0
- package/packages/pi-ai/dist/models/index.d.ts.map +1 -1
- package/packages/pi-ai/dist/models/index.js +8 -0
- package/packages/pi-ai/dist/models/index.js.map +1 -1
- package/packages/pi-ai/dist/providers/fake.d.ts +42 -0
- package/packages/pi-ai/dist/providers/fake.d.ts.map +1 -0
- package/packages/pi-ai/dist/providers/fake.js +319 -0
- package/packages/pi-ai/dist/providers/fake.js.map +1 -0
- package/packages/pi-ai/dist/providers/register-builtins.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/register-builtins.js +24 -0
- package/packages/pi-ai/dist/providers/register-builtins.js.map +1 -1
- package/packages/pi-ai/src/models/fake-model.ts +30 -0
- package/packages/pi-ai/src/models/index.ts +9 -0
- package/packages/pi-ai/src/providers/fake.ts +376 -0
- package/packages/pi-ai/src/providers/register-builtins.ts +23 -0
- package/packages/pi-ai/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js +74 -0
- package/packages/pi-coding-agent/dist/core/chat-controller-ordering.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js +14 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js +97 -0
- package/packages/pi-coding-agent/dist/core/extensions/runner.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-registry.js +5 -0
- package/packages/pi-coding-agent/dist/core/model-registry.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +4 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +8 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
- package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js +6 -4
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js +54 -15
- package/packages/pi-coding-agent/dist/modes/interactive/components/__tests__/tool-execution.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts +26 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js +112 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js +51 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/adaptive-layout.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/assistant-message.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js +10 -9
- package/packages/pi-coding-agent/dist/modes/interactive/components/chat-frame.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts +3 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js +11 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js +7 -6
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts +16 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +106 -17
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +60 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js +40 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.test.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +3 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +23 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js +20 -0
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js +79 -0
- package/packages/pi-coding-agent/dist/modes/interactive/slash-command-handlers.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js +13 -0
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme-schema.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js +18 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/theme.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js +36 -27
- package/packages/pi-coding-agent/dist/modes/interactive/theme/themes.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts +11 -0
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js +18 -0
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js +48 -0
- package/packages/pi-coding-agent/dist/modes/interactive/tui-mode.test.js.map +1 -0
- package/packages/pi-coding-agent/src/core/chat-controller-ordering.test.ts +87 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.test.ts +108 -0
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +16 -1
- package/packages/pi-coding-agent/src/core/model-registry.ts +4 -0
- package/packages/pi-coding-agent/src/core/settings-manager.ts +12 -0
- package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/chat-frame-compaction-tone.test.ts +7 -5
- package/packages/pi-coding-agent/src/modes/interactive/components/__tests__/tool-execution.test.ts +78 -15
- package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.test.ts +59 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/adaptive-layout.ts +160 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/assistant-message.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/chat-frame.ts +10 -9
- package/packages/pi-coding-agent/src/modes/interactive/components/settings-selector.ts +15 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-card-cleanup-and-success-runtime.test.ts +10 -9
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +118 -17
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.test.ts +43 -1
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +75 -1
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.test.ts +1 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +25 -0
- package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.test.ts +95 -0
- package/packages/pi-coding-agent/src/modes/interactive/slash-command-handlers.ts +24 -1
- package/packages/pi-coding-agent/src/modes/interactive/theme/theme-schema.ts +13 -0
- package/packages/pi-coding-agent/src/modes/interactive/theme/theme.ts +32 -2
- package/packages/pi-coding-agent/src/modes/interactive/theme/themes.ts +36 -27
- package/packages/pi-coding-agent/src/modes/interactive/tui-mode.test.ts +65 -0
- package/packages/pi-coding-agent/src/modes/interactive/tui-mode.ts +29 -0
- package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
- package/packages/pi-tui/dist/__tests__/style.test.d.ts +2 -0
- package/packages/pi-tui/dist/__tests__/style.test.d.ts.map +1 -0
- package/packages/pi-tui/dist/__tests__/style.test.js +63 -0
- package/packages/pi-tui/dist/__tests__/style.test.js.map +1 -0
- package/packages/pi-tui/dist/__tests__/tui.test.js +24 -3
- package/packages/pi-tui/dist/__tests__/tui.test.js.map +1 -1
- package/packages/pi-tui/dist/index.d.ts +1 -0
- package/packages/pi-tui/dist/index.d.ts.map +1 -1
- package/packages/pi-tui/dist/index.js +2 -0
- package/packages/pi-tui/dist/index.js.map +1 -1
- package/packages/pi-tui/dist/style.d.ts +41 -0
- package/packages/pi-tui/dist/style.d.ts.map +1 -0
- package/packages/pi-tui/dist/style.js +158 -0
- package/packages/pi-tui/dist/style.js.map +1 -0
- package/packages/pi-tui/dist/tui.d.ts +0 -1
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +3 -8
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/src/__tests__/style.test.ts +76 -0
- package/packages/pi-tui/src/__tests__/tui.test.ts +29 -3
- package/packages/pi-tui/src/index.ts +9 -0
- package/packages/pi-tui/src/style.ts +225 -0
- package/packages/pi-tui/src/tui.ts +3 -8
- package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
- package/pkg/dist/modes/interactive/theme/theme-schema.d.ts +12 -0
- package/pkg/dist/modes/interactive/theme/theme-schema.d.ts.map +1 -1
- package/pkg/dist/modes/interactive/theme/theme-schema.js +13 -0
- package/pkg/dist/modes/interactive/theme/theme-schema.js.map +1 -1
- package/pkg/dist/modes/interactive/theme/theme.d.ts +1 -1
- package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -1
- package/pkg/dist/modes/interactive/theme/theme.js +18 -1
- package/pkg/dist/modes/interactive/theme/theme.js.map +1 -1
- package/pkg/dist/modes/interactive/theme/themes.d.ts.map +1 -1
- package/pkg/dist/modes/interactive/theme/themes.js +36 -27
- package/pkg/dist/modes/interactive/theme/themes.js.map +1 -1
- package/src/resources/GSD-WORKFLOW.md +2 -2
- package/src/resources/extensions/github-sync/templates.ts +38 -8
- package/src/resources/extensions/github-sync/tests/inline-code.test.ts +66 -0
- package/src/resources/extensions/gsd/auto/loop-deps.ts +1 -0
- package/src/resources/extensions/gsd/auto/loop.ts +17 -10
- package/src/resources/extensions/gsd/auto/phases.ts +42 -28
- package/src/resources/extensions/gsd/auto/run-unit.ts +24 -14
- package/src/resources/extensions/gsd/auto-dashboard.ts +57 -8
- package/src/resources/extensions/gsd/auto-dispatch.ts +17 -0
- package/src/resources/extensions/gsd/auto-post-unit.ts +10 -10
- package/src/resources/extensions/gsd/auto-prompts.ts +116 -1
- package/src/resources/extensions/gsd/auto-recovery.ts +153 -7
- package/src/resources/extensions/gsd/auto-start.ts +7 -6
- package/src/resources/extensions/gsd/auto.ts +12 -1
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +16 -1
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +17 -1
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +135 -1
- package/src/resources/extensions/gsd/clean-root-preflight.ts +41 -3
- package/src/resources/extensions/gsd/commands-extract-learnings.ts +17 -12
- package/src/resources/extensions/gsd/custom-workflow-engine.ts +24 -1
- package/src/resources/extensions/gsd/db-base-schema.ts +15 -0
- package/src/resources/extensions/gsd/db-migration-steps.ts +17 -0
- package/src/resources/extensions/gsd/detection.ts +128 -0
- package/src/resources/extensions/gsd/graph.ts +12 -5
- package/src/resources/extensions/gsd/gsd-db.ts +119 -1
- package/src/resources/extensions/gsd/guided-flow.ts +2 -2
- package/src/resources/extensions/gsd/pr-evidence.ts +63 -5
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +7 -8
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +3 -1
- package/src/resources/extensions/gsd/safety/evidence-collector.ts +11 -2
- package/src/resources/extensions/gsd/tests/auto-abort-pause-regression.test.ts +7 -1
- package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +33 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +84 -5
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +170 -1
- package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +88 -2
- package/src/resources/extensions/gsd/tests/commands-extract-learnings.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +112 -6
- package/src/resources/extensions/gsd/tests/custom-workflow-engine.test.ts +40 -2
- package/src/resources/extensions/gsd/tests/db-migration-steps.integration.test.ts +428 -0
- package/src/resources/extensions/gsd/tests/db-schema-metadata.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/detection.test.ts +140 -0
- package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-basic.md +52 -0
- package/src/resources/extensions/gsd/tests/fixtures/pr-body/commands-ship-empty-optionals.md +42 -0
- package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-no-blockers.md +55 -0
- package/src/resources/extensions/gsd/tests/fixtures/pr-body/swarm-lane-with-blockers.md +60 -0
- package/src/resources/extensions/gsd/tests/graph-operations.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/has-pending-deep-stage.test.ts +33 -1
- package/src/resources/extensions/gsd/tests/pr-evidence-equivalence.test.ts +102 -0
- package/src/resources/extensions/gsd/tests/pr-evidence-hardening.test.ts +165 -0
- package/src/resources/extensions/gsd/tests/right-sized-workflow-prompts.test.ts +192 -0
- package/src/resources/extensions/gsd/tests/safety-harness-false-positives.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +46 -2
- package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/working-output-messages.test.ts +93 -0
- package/src/resources/extensions/gsd/tests/worktree-health-dispatch.test.ts +37 -6
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/worktree-nested-git-safety.test.ts +9 -2
- package/src/resources/extensions/gsd/tests/worktree-write-gate.test.ts +179 -0
- package/src/resources/extensions/gsd/working-output-messages.ts +120 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +15 -4
- package/packages/contracts/tsconfig.tsbuildinfo +0 -1
- /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → mPZbi5BH9dwokaPZlrYuQ}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{bQDK5_LtkGVS64AirQgQG → mPZbi5BH9dwokaPZlrYuQ}/_ssgManifest.js +0 -0
|
@@ -11,6 +11,7 @@ export interface PrEvidenceInput {
|
|
|
11
11
|
changeType?: PrChangeType;
|
|
12
12
|
linkedIssue?: string;
|
|
13
13
|
summaries?: string[];
|
|
14
|
+
blockers?: string[];
|
|
14
15
|
roadmapItems?: string[];
|
|
15
16
|
metrics?: string[];
|
|
16
17
|
testsRun?: string[];
|
|
@@ -34,8 +35,54 @@ const CHANGE_TYPE_LABELS: Record<PrChangeType, string> = {
|
|
|
34
35
|
chore: "Build, CI, or tooling changes",
|
|
35
36
|
};
|
|
36
37
|
|
|
38
|
+
// Per-item cap for user-supplied content. 2 KB gives slice titles plus
|
|
39
|
+
// descriptions room while still bounding malicious DoS-via-PR-body input.
|
|
40
|
+
const USER_CONTENT_CAP_BYTES = 2048;
|
|
41
|
+
const TRUNCATION_SUFFIX = " … [truncated]";
|
|
42
|
+
|
|
43
|
+
// Strips HTML comments, fake commit trailers (Co-Authored-By, Signed-off-by —
|
|
44
|
+
// case-insensitive on the trailer name), and caps total length. Designed to
|
|
45
|
+
// be a no-op for well-formed input; golden fixtures must remain byte-stable.
|
|
46
|
+
// Trailer lines are removed (not rejected) so that a single bad line in an
|
|
47
|
+
// otherwise legitimate description does not block the entire PR.
|
|
48
|
+
function sanitizeUserContent(s: string): string {
|
|
49
|
+
if (!s) return s;
|
|
50
|
+
// Strip HTML comments greedily across newlines.
|
|
51
|
+
let out = s.replace(/<!--[\s\S]*?-->/g, "");
|
|
52
|
+
// Drop lines that look like commit trailers we do not want forged.
|
|
53
|
+
out = out
|
|
54
|
+
.split("\n")
|
|
55
|
+
.filter((line) => !/^\s*(co-authored-by|signed-off-by)\s*:/i.test(line))
|
|
56
|
+
.join("\n");
|
|
57
|
+
if (Buffer.byteLength(out, "utf8") > USER_CONTENT_CAP_BYTES) {
|
|
58
|
+
const budget = USER_CONTENT_CAP_BYTES - Buffer.byteLength(TRUNCATION_SUFFIX, "utf8");
|
|
59
|
+
// Truncate by code units to stay safely under the byte budget for ASCII;
|
|
60
|
+
// for multibyte content we conservatively walk back until under budget.
|
|
61
|
+
let sliced = out.slice(0, Math.max(0, budget));
|
|
62
|
+
while (Buffer.byteLength(sliced, "utf8") > budget && sliced.length > 0) {
|
|
63
|
+
sliced = sliced.slice(0, -1);
|
|
64
|
+
}
|
|
65
|
+
out = sliced + TRUNCATION_SUFFIX;
|
|
66
|
+
}
|
|
67
|
+
return out;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Strips HTML comments and fake trailers without applying the length cap.
|
|
71
|
+
// Used for short fields like linkedIssue where truncation would be confusing.
|
|
72
|
+
function sanitizeIssueRef(s: string): string {
|
|
73
|
+
if (!s) return s;
|
|
74
|
+
let out = s.replace(/<!--[\s\S]*?-->/g, "");
|
|
75
|
+
out = out
|
|
76
|
+
.split("\n")
|
|
77
|
+
.filter((line) => !/^\s*(co-authored-by|signed-off-by)\s*:/i.test(line))
|
|
78
|
+
.join("\n");
|
|
79
|
+
return out;
|
|
80
|
+
}
|
|
81
|
+
|
|
37
82
|
function normalizeList(values: readonly string[] | undefined): string[] {
|
|
38
|
-
return (values ?? [])
|
|
83
|
+
return (values ?? [])
|
|
84
|
+
.map((value) => sanitizeUserContent(value).trim())
|
|
85
|
+
.filter(Boolean);
|
|
39
86
|
}
|
|
40
87
|
|
|
41
88
|
function changeTypeChecklist(selected: PrChangeType): string[] {
|
|
@@ -56,13 +103,17 @@ export function buildPrEvidence(input: PrEvidenceInput): PrEvidence {
|
|
|
56
103
|
const subjectTitle = input.milestoneTitle?.trim() || subjectId;
|
|
57
104
|
const changeType = input.changeType ?? "feat";
|
|
58
105
|
const summaries = normalizeList(input.summaries);
|
|
106
|
+
const blockers = normalizeList(input.blockers);
|
|
59
107
|
const roadmapItems = normalizeList(input.roadmapItems);
|
|
60
108
|
const metrics = normalizeList(input.metrics);
|
|
61
109
|
const testsRun = normalizeList(input.testsRun);
|
|
62
110
|
const rollbackNotes = normalizeList(input.rollbackNotes);
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
const
|
|
111
|
+
// linkedIssue is sanitized but not length-capped: legitimate issue refs
|
|
112
|
+
// are short by nature, and truncating "Closes #123" would be unhelpful.
|
|
113
|
+
const linkedIssueRaw = input.linkedIssue ? sanitizeIssueRef(input.linkedIssue).trim() : "";
|
|
114
|
+
const linkedIssue = linkedIssueRaw || "Not specified. Add an issue link before marking this PR ready if CONTRIBUTING.md requires one.";
|
|
115
|
+
const why = (input.why ? sanitizeUserContent(input.why).trim() : "") || `${capitalize(subjectKind)} work is complete and ready for review.`;
|
|
116
|
+
const how = (input.how ? sanitizeUserContent(input.how).trim() : "") || "Generated from GSD evidence and local workflow artifacts.";
|
|
66
117
|
const title = `${changeType}: ${subjectTitle}`;
|
|
67
118
|
|
|
68
119
|
const sections: string[] = [
|
|
@@ -75,6 +126,13 @@ export function buildPrEvidence(input: PrEvidenceInput): PrEvidence {
|
|
|
75
126
|
"## What",
|
|
76
127
|
"",
|
|
77
128
|
summaries.length > 0 ? summaries.join("\n\n") : `${capitalize(subjectKind)} ${subjectId} completed.`,
|
|
129
|
+
];
|
|
130
|
+
|
|
131
|
+
if (blockers.length > 0) {
|
|
132
|
+
sections.push("", "## Blockers", "", blockers.map((blocker) => `- ${blocker}`).join("\n"));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
sections.push(
|
|
78
136
|
"",
|
|
79
137
|
"## Why",
|
|
80
138
|
"",
|
|
@@ -87,7 +145,7 @@ export function buildPrEvidence(input: PrEvidenceInput): PrEvidence {
|
|
|
87
145
|
"## Linked Issue",
|
|
88
146
|
"",
|
|
89
147
|
linkedIssue,
|
|
90
|
-
|
|
148
|
+
);
|
|
91
149
|
|
|
92
150
|
if (roadmapItems.length > 0) {
|
|
93
151
|
sections.push("", "## Roadmap", "", roadmapItems.join("\n"));
|
|
@@ -16,15 +16,14 @@ Start with what the excerpts give you. Read full files when the section heads si
|
|
|
16
16
|
|
|
17
17
|
**On-demand Read ordering:** Complete all slice SUMMARY Reads you need for cross-slice synthesis, the Decision Re-evaluation table, and LEARNINGS **before** calling `gsd_complete_milestone` (step 12). Once that tool runs, the milestone is marked complete in the DB, so it must be the final persistent milestone-closeout write.
|
|
18
18
|
|
|
19
|
-
###
|
|
19
|
+
### Closeout Review Mode
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
The inlined context includes a validation status block.
|
|
22
22
|
|
|
23
|
-
-
|
|
24
|
-
-
|
|
25
|
-
- Significant tests added or changed -> **tester** coverage check against success criteria.
|
|
23
|
+
- If it says a passing validation artifact is present, treat that artifact as authoritative for success criteria, requirement coverage, verification classes, and cross-slice integration. Do not delegate fresh reviewer/security/tester audits unless the validation artifact is internally inconsistent with the inlined summaries.
|
|
24
|
+
- If validation is missing, stale, non-pass, or internally inconsistent, use `subagent` for review work needing fresh context before drafting LEARNINGS: cross-slice integrations or new public APIs -> **reviewer**; auth, network, parsing, file IO, shell exec, or crypto -> **security**; significant tests added or changed -> **tester**.
|
|
26
25
|
|
|
27
|
-
Subagents report only; they do not write user source. Fold findings into Decision Re-evaluation and LEARNINGS before completion.
|
|
26
|
+
Subagents report only; they do not write user source. Fold any findings into Decision Re-evaluation and LEARNINGS before completion.
|
|
28
27
|
|
|
29
28
|
{{inlinedContext}}
|
|
30
29
|
|
|
@@ -33,8 +32,8 @@ Subagents report only; they do not write user source. Fold findings into Decisio
|
|
|
33
32
|
1. Use the **Milestone Summary** output template from the inlined context above
|
|
34
33
|
2. {{skillActivation}}
|
|
35
34
|
3. **Verify code changes exist.** Compare milestone work against the integration branch (`main`, `master`, or recorded branch), using merge-base as older revision and `HEAD` as newer. If the diff lists non-`.gsd/` files, pass. If `HEAD` equals the integration branch/merge-base, treat it as a self-diff retry: inspect milestone-scoped commit evidence (`GSD-Unit: {{milestoneId}}` or production `GSD-Task: Sxx/Tyy` trailers touching `.gsd/milestones/{{milestoneId}}/`) and verify those commits touched non-`.gsd/` files. Record **verification failure** only when neither source shows implementation files.
|
|
36
|
-
4. Verify every **success criterion** from `{{roadmapPath}}
|
|
37
|
-
5. Verify **definition of done**: all slices `[x]`, summaries exist, and integrations work. Record unmet items as **verification failure**.
|
|
35
|
+
4. Verify every **success criterion** from `{{roadmapPath}}`. If passing validation is present, summarize the validation evidence instead of re-auditing it; otherwise verify with evidence from summaries, tests, or observable behavior. Record unmet criteria as **verification failure**.
|
|
36
|
+
5. Verify **definition of done**: all slices `[x]`, summaries exist, and integrations work. If passing validation is present, trust its integration/verification verdict unless inconsistent with current artifacts. Record unmet items as **verification failure**.
|
|
38
37
|
6. If the roadmap includes a **Horizontal Checklist**, verify each item and note unchecked items in the summary.
|
|
39
38
|
7. Fill the **Decision Re-evaluation** table: compare each key `.gsd/DECISIONS.md` decision from this milestone with what shipped, and flag decisions to revisit.
|
|
40
39
|
8. Validate **requirement status transitions**. For each changed requirement, confirm evidence supports the new status. Requirements may move between Active, Validated, Deferred, Blocked, or Out of Scope only with proof.
|
|
@@ -48,7 +48,7 @@ Narrate decomposition reasoning in complete sentences: grouping, risk order, ver
|
|
|
48
48
|
Then:
|
|
49
49
|
1. Use the **Roadmap** output template from the inlined context above
|
|
50
50
|
2. {{skillActivation}}
|
|
51
|
-
3. Create only as many demoable vertical slices as the work genuinely needs.
|
|
51
|
+
3. Create only as many demoable vertical slices as the work genuinely needs. Use 1-10 slices, sized to the work; tiny/single-file/static work should usually be one slice.
|
|
52
52
|
4. Order by risk, high-risk first.
|
|
53
53
|
5. Call `gsd_plan_milestone` to persist milestone fields, slice rows, and **Horizontal Checklist** through the DB-backed path. Fill checklist concerns considered during planning: requirements, decisions, shutdown, revenue, auth, shared resources, reconnection. Omit for trivial milestones. Do **not** write `{{outputPath}}`, `ROADMAP.md`, or other planning artifacts manually; the tool owns rendering and persistence.
|
|
54
54
|
6. If planning produced structural decisions (slice ordering, technology choices, scope exclusions), call `gsd_decision_save` for each; the tool assigns IDs and regenerates `.gsd/DECISIONS.md`.
|
|
@@ -78,6 +78,8 @@ Apply these when decomposing and ordering slices:
|
|
|
78
78
|
- Ship features, not proofs; use clearly marked realistic stubs only when necessary.
|
|
79
79
|
- **Dependency format is comma-separated, never range syntax.** Write `depends:[S01,S02,S03]`, not `depends:[S01-S03]`.
|
|
80
80
|
- Roadmap ambition must match the milestone; right-size decomposition.
|
|
81
|
+
- Missing ecosystem markers are not a reason to over-plan. If Project Classification says `untyped-existing`, treat the listed content files as the project surface and use generic file-level workflow guidance.
|
|
82
|
+
- For `untyped-existing` projects with 1-2 content files, prefer exactly one slice unless the request clearly spans multiple independent user-visible capabilities. For 3-5 content files, prefer 1-2 slices.
|
|
81
83
|
|
|
82
84
|
## Progressive Planning (ADR-011)
|
|
83
85
|
|
|
@@ -50,6 +50,15 @@ export interface FileEditEvidence {
|
|
|
50
50
|
|
|
51
51
|
export type EvidenceEntry = BashEvidence | FileWriteEvidence | FileEditEvidence;
|
|
52
52
|
|
|
53
|
+
const EXECUTION_TOOL_NAMES = new Set([
|
|
54
|
+
"bash",
|
|
55
|
+
"Bash",
|
|
56
|
+
"gsd_exec",
|
|
57
|
+
"gsd_exec_search",
|
|
58
|
+
"mcp__gsd-workflow__gsd_exec",
|
|
59
|
+
"mcp__gsd-workflow__gsd_exec_search",
|
|
60
|
+
]);
|
|
61
|
+
|
|
53
62
|
// ─── Module State ───────────────────────────────────────────────────────────
|
|
54
63
|
|
|
55
64
|
let unitEvidence: EvidenceEntry[] = [];
|
|
@@ -188,11 +197,11 @@ export function clearEvidenceFromDisk(
|
|
|
188
197
|
* Exit codes and output are filled in by recordToolResult after execution.
|
|
189
198
|
*/
|
|
190
199
|
export function recordToolCall(toolCallId: string, toolName: string, input: Record<string, unknown>): void {
|
|
191
|
-
if (toolName
|
|
200
|
+
if (EXECUTION_TOOL_NAMES.has(toolName)) {
|
|
192
201
|
unitEvidence.push({
|
|
193
202
|
kind: "bash",
|
|
194
203
|
toolCallId,
|
|
195
|
-
command: String(input.command ?? ""),
|
|
204
|
+
command: String(input.command ?? input.cmd ?? input.query ?? ""),
|
|
196
205
|
exitCode: -1,
|
|
197
206
|
outputSnippet: "",
|
|
198
207
|
timestamp: Date.now(),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import test from "node:test";
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
3
|
|
|
4
|
-
import { _buildAbortedPauseContext } from "../bootstrap/agent-end-recovery.js";
|
|
4
|
+
import { _buildAbortedPauseContext, isUserInitiatedAbortMessage } from "../bootstrap/agent-end-recovery.js";
|
|
5
5
|
import { _buildCancelledUnitStopReason } from "../auto/phases.js";
|
|
6
6
|
|
|
7
7
|
test("aborted agent_end maps errorMessage into structured aborted pause context", () => {
|
|
@@ -30,3 +30,9 @@ test("cancelled non-session failures are labeled as unit aborts (not session-cre
|
|
|
30
30
|
assert.equal(cancelled.stopReason, "Unit aborted: tool invocation cancelled");
|
|
31
31
|
assert.equal(cancelled.loopReason, "unit-aborted");
|
|
32
32
|
});
|
|
33
|
+
|
|
34
|
+
test("provider user-abort errors are recognized as cancellations, not provider outages", () => {
|
|
35
|
+
assert.equal(isUserInitiatedAbortMessage("Claude Code process aborted by user"), true);
|
|
36
|
+
assert.equal(isUserInitiatedAbortMessage("Request aborted by user"), true);
|
|
37
|
+
assert.equal(isUserInitiatedAbortMessage("HTTP 503 Service Unavailable"), false);
|
|
38
|
+
});
|
|
@@ -23,6 +23,8 @@ import {
|
|
|
23
23
|
_refreshLastCommitForTests,
|
|
24
24
|
_getLastCommitForTests,
|
|
25
25
|
_getLastCommitFetchedAtForTests,
|
|
26
|
+
formatRuntimeHealthSignal,
|
|
27
|
+
shouldRenderRoadmapProgress,
|
|
26
28
|
} from "../auto-dashboard.ts";
|
|
27
29
|
import {
|
|
28
30
|
openDatabase,
|
|
@@ -193,6 +195,37 @@ test("formatWidgetTokens formats millions with M", () => {
|
|
|
193
195
|
assert.equal(formatWidgetTokens(25_000_000), "25M");
|
|
194
196
|
});
|
|
195
197
|
|
|
198
|
+
test("formatRuntimeHealthSignal surfaces idle recovery instead of generic progress", () => {
|
|
199
|
+
const signal = formatRuntimeHealthSignal({
|
|
200
|
+
version: 1,
|
|
201
|
+
unitType: "research-milestone",
|
|
202
|
+
unitId: "M001",
|
|
203
|
+
startedAt: 1_000,
|
|
204
|
+
updatedAt: 600_000,
|
|
205
|
+
phase: "recovered",
|
|
206
|
+
wrapupWarningSent: false,
|
|
207
|
+
continueHereFired: false,
|
|
208
|
+
timeoutAt: null,
|
|
209
|
+
lastProgressAt: 1_000,
|
|
210
|
+
progressCount: 1,
|
|
211
|
+
lastProgressKind: "idle-recovery-retry",
|
|
212
|
+
recoveryAttempts: 1,
|
|
213
|
+
lastRecoveryReason: "idle",
|
|
214
|
+
}, 600_000);
|
|
215
|
+
|
|
216
|
+
assert.deepEqual(signal, {
|
|
217
|
+
level: "yellow",
|
|
218
|
+
summary: "Recovering",
|
|
219
|
+
detail: "retry 1 after idle stall",
|
|
220
|
+
});
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
test("shouldRenderRoadmapProgress hides pre-roadmap zero-slice progress", () => {
|
|
224
|
+
assert.equal(shouldRenderRoadmapProgress(null), false);
|
|
225
|
+
assert.equal(shouldRenderRoadmapProgress({ done: 0, total: 0, activeSliceTasks: null } as any), false);
|
|
226
|
+
assert.equal(shouldRenderRoadmapProgress({ done: 0, total: 1, activeSliceTasks: null } as any), true);
|
|
227
|
+
});
|
|
228
|
+
|
|
196
229
|
// ─── estimateTimeRemaining ──────────────────────────────────────────────
|
|
197
230
|
|
|
198
231
|
test("estimateTimeRemaining returns null when no ledger data", () => {
|
|
@@ -879,7 +879,11 @@ test("autoLoop passes structured session-lock failure details to the handler", a
|
|
|
879
879
|
);
|
|
880
880
|
});
|
|
881
881
|
|
|
882
|
-
|
|
882
|
+
// Regression for #5308: the iteration prelude must dequeue sidecar items
|
|
883
|
+
// (popping the queue and emitting the `sidecar-dequeue` journal event) BEFORE
|
|
884
|
+
// validateSessionLock + break-on-invalid. Inverting that order silently drops
|
|
885
|
+
// queued sidecar work on lock-loss. Covers first-iteration and mid-session.
|
|
886
|
+
test("autoLoop dequeues sidecar item before session-lock break (first iteration, #5308)", async () => {
|
|
883
887
|
_resetPendingResolve();
|
|
884
888
|
|
|
885
889
|
const ctx = makeMockCtx();
|
|
@@ -911,12 +915,87 @@ test("autoLoop keeps queued sidecar work when the session lock is lost", async (
|
|
|
911
915
|
|
|
912
916
|
await autoLoop(ctx, pi, s, deps);
|
|
913
917
|
|
|
914
|
-
assert.equal(
|
|
915
|
-
|
|
916
|
-
|
|
918
|
+
assert.equal(
|
|
919
|
+
s.sidecarQueue.length,
|
|
920
|
+
0,
|
|
921
|
+
"sidecar item must be popped on lock-loss iteration (pre-#5308 ordering)",
|
|
922
|
+
);
|
|
923
|
+
assert.ok(
|
|
924
|
+
journalEvents.includes("sidecar-dequeue"),
|
|
925
|
+
"sidecar-dequeue journal event must be emitted before session-lock break",
|
|
926
|
+
);
|
|
927
|
+
assert.ok(
|
|
928
|
+
deps.callLog.includes("handleLostSessionLock"),
|
|
929
|
+
"session lock handler must still fire after sidecar dequeue",
|
|
930
|
+
);
|
|
917
931
|
assert.ok(!deps.callLog.includes("deriveState"), "lock loss should stop before deriving state");
|
|
918
932
|
});
|
|
919
933
|
|
|
934
|
+
test("autoLoop dequeues sidecar item before session-lock break (mid-session, #5308)", async () => {
|
|
935
|
+
_resetPendingResolve();
|
|
936
|
+
|
|
937
|
+
const ctx = makeMockCtx();
|
|
938
|
+
ctx.ui.setStatus = () => {};
|
|
939
|
+
const pi = makeMockPi();
|
|
940
|
+
const s = makeLoopSession();
|
|
941
|
+
|
|
942
|
+
const journalEvents: string[] = [];
|
|
943
|
+
let lockCheckCount = 0;
|
|
944
|
+
const deps = makeMockDeps({
|
|
945
|
+
// First iteration: lock valid; second iteration: lock invalidates.
|
|
946
|
+
validateSessionLock: () => {
|
|
947
|
+
lockCheckCount += 1;
|
|
948
|
+
if (lockCheckCount === 1) {
|
|
949
|
+
return { valid: true } as SessionLockStatus;
|
|
950
|
+
}
|
|
951
|
+
return {
|
|
952
|
+
valid: false,
|
|
953
|
+
failureReason: "compromised",
|
|
954
|
+
expectedPid: process.pid,
|
|
955
|
+
} as SessionLockStatus;
|
|
956
|
+
},
|
|
957
|
+
handleLostSessionLock: () => {
|
|
958
|
+
deps.callLog.push("handleLostSessionLock");
|
|
959
|
+
},
|
|
960
|
+
emitJournalEvent: (entry) => {
|
|
961
|
+
journalEvents.push(entry.eventType);
|
|
962
|
+
},
|
|
963
|
+
// Enqueue a sidecar item at the end of iteration 1, so iteration 2 begins
|
|
964
|
+
// with a non-empty queue and an invalid lock.
|
|
965
|
+
postUnitPostVerification: async () => {
|
|
966
|
+
deps.callLog.push("postUnitPostVerification");
|
|
967
|
+
s.sidecarQueue.push({
|
|
968
|
+
kind: "hook" as const,
|
|
969
|
+
unitType: "hook/review",
|
|
970
|
+
unitId: "M001/S01/T01/review",
|
|
971
|
+
prompt: "review the code",
|
|
972
|
+
});
|
|
973
|
+
return "continue" as const;
|
|
974
|
+
},
|
|
975
|
+
});
|
|
976
|
+
|
|
977
|
+
const loopPromise = autoLoop(ctx, pi, s, deps);
|
|
978
|
+
// Allow the loop to reach runUnit's await on iteration 1.
|
|
979
|
+
await new Promise((r) => setTimeout(r, 50));
|
|
980
|
+
resolveAgentEnd(makeEvent());
|
|
981
|
+
await loopPromise;
|
|
982
|
+
|
|
983
|
+
assert.ok(lockCheckCount >= 2, "lock validator must run on iteration 2");
|
|
984
|
+
assert.equal(
|
|
985
|
+
s.sidecarQueue.length,
|
|
986
|
+
0,
|
|
987
|
+
"queued sidecar item must be popped on the lock-loss iteration",
|
|
988
|
+
);
|
|
989
|
+
assert.ok(
|
|
990
|
+
journalEvents.includes("sidecar-dequeue"),
|
|
991
|
+
"sidecar-dequeue journal event must be emitted before session-lock break",
|
|
992
|
+
);
|
|
993
|
+
assert.ok(
|
|
994
|
+
deps.callLog.includes("handleLostSessionLock"),
|
|
995
|
+
"lock-loss handler must still fire on iteration 2",
|
|
996
|
+
);
|
|
997
|
+
});
|
|
998
|
+
|
|
920
999
|
test("autoLoop exits on terminal blocked state", async (t) => {
|
|
921
1000
|
_resetPendingResolve();
|
|
922
1001
|
|
|
@@ -2477,7 +2556,7 @@ test("autoLoop warns but proceeds for greenfield project (no project files) (#18
|
|
|
2477
2556
|
"should not stop with health check failure for greenfield project",
|
|
2478
2557
|
);
|
|
2479
2558
|
const greenfieldWarning = notifications.find(
|
|
2480
|
-
(n) => n.includes("no
|
|
2559
|
+
(n) => n.includes("no project content yet") && n.includes("greenfield"),
|
|
2481
2560
|
);
|
|
2482
2561
|
assert.ok(
|
|
2483
2562
|
greenfieldWarning,
|
|
@@ -7,11 +7,12 @@ import { randomUUID } from "node:crypto";
|
|
|
7
7
|
|
|
8
8
|
import { verifyExpectedArtifact, hasImplementationArtifacts, resolveExpectedArtifactPath, diagnoseExpectedArtifact, buildLoopRemediationSteps, writeBlockerPlaceholder } from "../auto-recovery.ts";
|
|
9
9
|
import { resolveMilestoneFile } from "../paths.ts";
|
|
10
|
-
import { openDatabase, closeDatabase, insertMilestone, insertSlice, insertGateRow, insertTask } from "../gsd-db.ts";
|
|
10
|
+
import { openDatabase, closeDatabase, insertMilestone, insertSlice, insertGateRow, insertTask, getMilestoneCommitAttributionShas } from "../gsd-db.ts";
|
|
11
11
|
import { clearParseCache } from "../files.ts";
|
|
12
12
|
import { parseRoadmap } from "../parsers-legacy.ts";
|
|
13
13
|
import { invalidateAllCaches } from "../cache.ts";
|
|
14
14
|
import { deriveState, invalidateStateCache } from "../state.ts";
|
|
15
|
+
import { writeIntegrationBranch } from "../git-service.ts";
|
|
15
16
|
|
|
16
17
|
const tmpDirs: string[] = [];
|
|
17
18
|
|
|
@@ -735,6 +736,174 @@ test("hasImplementationArtifacts rejects milestone-scoped main history with only
|
|
|
735
736
|
}
|
|
736
737
|
});
|
|
737
738
|
|
|
739
|
+
test("hasImplementationArtifacts finds integration implementation-only commits when milestone branch diff is .gsd-only", () => {
|
|
740
|
+
const base = makeGitBase();
|
|
741
|
+
try {
|
|
742
|
+
mkdirSync(join(base, "src"), { recursive: true });
|
|
743
|
+
writeFileSync(join(base, "src", "feature.ts"), "export function feature() {}\n");
|
|
744
|
+
execFileSync("git", ["add", "src/feature.ts"], { cwd: base, stdio: "ignore" });
|
|
745
|
+
execFileSync("git", ["commit", "-m", "feat: add milestone feature\n\nGSD-Task: S01/T01"], { cwd: base, stdio: "ignore" });
|
|
746
|
+
|
|
747
|
+
mkdirSync(join(base, ".gsd"), { recursive: true });
|
|
748
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
749
|
+
insertMilestone({ id: "M001", title: "Milestone One", status: "active" });
|
|
750
|
+
insertSlice({
|
|
751
|
+
id: "S01",
|
|
752
|
+
milestoneId: "M001",
|
|
753
|
+
title: "Slice One",
|
|
754
|
+
status: "complete",
|
|
755
|
+
risk: "low",
|
|
756
|
+
depends: [],
|
|
757
|
+
});
|
|
758
|
+
insertTask({
|
|
759
|
+
id: "T01",
|
|
760
|
+
sliceId: "S01",
|
|
761
|
+
milestoneId: "M001",
|
|
762
|
+
title: "Task One",
|
|
763
|
+
status: "complete",
|
|
764
|
+
});
|
|
765
|
+
|
|
766
|
+
execFileSync("git", ["checkout", "-b", "milestone/M001"], { cwd: base, stdio: "ignore" });
|
|
767
|
+
writeIntegrationBranch(base, "M001", "main");
|
|
768
|
+
writeFileSync(join(base, ".gsd", "milestones", "M001", "M001-SUMMARY.md"), "# Milestone Summary\nDone.");
|
|
769
|
+
execFileSync("git", ["add", "."], { cwd: base, stdio: "ignore" });
|
|
770
|
+
execFileSync("git", ["commit", "-m", "chore: auto-commit after complete-milestone\n\nGSD-Unit: M001"], { cwd: base, stdio: "ignore" });
|
|
771
|
+
|
|
772
|
+
const result = hasImplementationArtifacts(base, "M001");
|
|
773
|
+
assert.equal(
|
|
774
|
+
result,
|
|
775
|
+
"present",
|
|
776
|
+
".gsd-only milestone closeout diffs should still honor implementation commits already on the integration branch",
|
|
777
|
+
);
|
|
778
|
+
} finally {
|
|
779
|
+
cleanup(base);
|
|
780
|
+
}
|
|
781
|
+
});
|
|
782
|
+
|
|
783
|
+
test("hasImplementationArtifacts backfills untagged main implementation commits from completed task file hints", () => {
|
|
784
|
+
const base = makeGitBase();
|
|
785
|
+
try {
|
|
786
|
+
mkdirSync(join(base, ".gsd"), { recursive: true });
|
|
787
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
788
|
+
insertMilestone({ id: "M001", title: "Milestone One", status: "active" });
|
|
789
|
+
insertSlice({
|
|
790
|
+
id: "S01",
|
|
791
|
+
milestoneId: "M001",
|
|
792
|
+
title: "Slice One",
|
|
793
|
+
status: "complete",
|
|
794
|
+
risk: "low",
|
|
795
|
+
depends: [],
|
|
796
|
+
});
|
|
797
|
+
insertTask({
|
|
798
|
+
id: "T01",
|
|
799
|
+
sliceId: "S01",
|
|
800
|
+
milestoneId: "M001",
|
|
801
|
+
title: "Task One",
|
|
802
|
+
status: "complete",
|
|
803
|
+
keyFiles: ["index.html", "style.css", "app.js"],
|
|
804
|
+
planning: { files: ["index.html", "style.css", "app.js"] },
|
|
805
|
+
});
|
|
806
|
+
|
|
807
|
+
writeFileSync(join(base, "index.html"), "<main></main>\n");
|
|
808
|
+
writeFileSync(join(base, "style.css"), "main { display: block; }\n");
|
|
809
|
+
writeFileSync(join(base, "app.js"), "document.body.dataset.ready = 'true';\n");
|
|
810
|
+
execFileSync("git", ["add", "index.html", "style.css", "app.js"], { cwd: base, stdio: "ignore" });
|
|
811
|
+
execFileSync("git", ["commit", "-m", "feat: add to-do app with CRUD and localStorage persistence"], { cwd: base, stdio: "ignore" });
|
|
812
|
+
const commitSha = execFileSync("git", ["rev-parse", "HEAD"], { cwd: base, encoding: "utf-8" }).trim();
|
|
813
|
+
|
|
814
|
+
const result = hasImplementationArtifacts(base, "M001");
|
|
815
|
+
assert.equal(
|
|
816
|
+
result,
|
|
817
|
+
"present",
|
|
818
|
+
"completed task file hints should repair prior untagged implementation commits on main",
|
|
819
|
+
);
|
|
820
|
+
assert.deepEqual(getMilestoneCommitAttributionShas("M001"), [commitSha]);
|
|
821
|
+
} finally {
|
|
822
|
+
cleanup(base);
|
|
823
|
+
}
|
|
824
|
+
});
|
|
825
|
+
|
|
826
|
+
test("hasImplementationArtifacts does not backfill untagged commits before milestone creation", () => {
|
|
827
|
+
const base = makeGitBase();
|
|
828
|
+
try {
|
|
829
|
+
writeFileSync(join(base, "app.js"), "document.body.dataset.ready = 'old';\n");
|
|
830
|
+
execFileSync("git", ["add", "app.js"], { cwd: base, stdio: "ignore" });
|
|
831
|
+
execFileSync("git", ["commit", "-m", "feat: old app work"], {
|
|
832
|
+
cwd: base,
|
|
833
|
+
stdio: "ignore",
|
|
834
|
+
env: {
|
|
835
|
+
...process.env,
|
|
836
|
+
GIT_AUTHOR_DATE: "2020-01-01T00:00:00Z",
|
|
837
|
+
GIT_COMMITTER_DATE: "2020-01-01T00:00:00Z",
|
|
838
|
+
},
|
|
839
|
+
});
|
|
840
|
+
|
|
841
|
+
mkdirSync(join(base, ".gsd"), { recursive: true });
|
|
842
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
843
|
+
insertMilestone({ id: "M001", title: "Milestone One", status: "active" });
|
|
844
|
+
insertSlice({
|
|
845
|
+
id: "S01",
|
|
846
|
+
milestoneId: "M001",
|
|
847
|
+
title: "Slice One",
|
|
848
|
+
status: "complete",
|
|
849
|
+
risk: "low",
|
|
850
|
+
depends: [],
|
|
851
|
+
});
|
|
852
|
+
insertTask({
|
|
853
|
+
id: "T01",
|
|
854
|
+
sliceId: "S01",
|
|
855
|
+
milestoneId: "M001",
|
|
856
|
+
title: "Task One",
|
|
857
|
+
status: "complete",
|
|
858
|
+
keyFiles: ["app.js"],
|
|
859
|
+
planning: { files: ["app.js"] },
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
const result = hasImplementationArtifacts(base, "M001");
|
|
863
|
+
assert.equal(result, "absent", "pre-milestone commits must not be attributed to the milestone");
|
|
864
|
+
assert.deepEqual(getMilestoneCommitAttributionShas("M001"), []);
|
|
865
|
+
} finally {
|
|
866
|
+
cleanup(base);
|
|
867
|
+
}
|
|
868
|
+
});
|
|
869
|
+
|
|
870
|
+
test("hasImplementationArtifacts does not backfill unrelated untagged implementation commits", () => {
|
|
871
|
+
const base = makeGitBase();
|
|
872
|
+
try {
|
|
873
|
+
mkdirSync(join(base, ".gsd"), { recursive: true });
|
|
874
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
875
|
+
insertMilestone({ id: "M001", title: "Milestone One", status: "active" });
|
|
876
|
+
insertSlice({
|
|
877
|
+
id: "S01",
|
|
878
|
+
milestoneId: "M001",
|
|
879
|
+
title: "Slice One",
|
|
880
|
+
status: "complete",
|
|
881
|
+
risk: "low",
|
|
882
|
+
depends: [],
|
|
883
|
+
});
|
|
884
|
+
insertTask({
|
|
885
|
+
id: "T01",
|
|
886
|
+
sliceId: "S01",
|
|
887
|
+
milestoneId: "M001",
|
|
888
|
+
title: "Task One",
|
|
889
|
+
status: "complete",
|
|
890
|
+
keyFiles: ["src/expected.ts"],
|
|
891
|
+
planning: { files: ["src/expected.ts"] },
|
|
892
|
+
});
|
|
893
|
+
|
|
894
|
+
mkdirSync(join(base, "src"), { recursive: true });
|
|
895
|
+
writeFileSync(join(base, "src", "unrelated.ts"), "export const unrelated = true;\n");
|
|
896
|
+
execFileSync("git", ["add", "src/unrelated.ts"], { cwd: base, stdio: "ignore" });
|
|
897
|
+
execFileSync("git", ["commit", "-m", "feat: unrelated work"], { cwd: base, stdio: "ignore" });
|
|
898
|
+
|
|
899
|
+
const result = hasImplementationArtifacts(base, "M001");
|
|
900
|
+
assert.equal(result, "absent", "backfill must require overlap with completed task file hints");
|
|
901
|
+
assert.deepEqual(getMilestoneCommitAttributionShas("M001"), []);
|
|
902
|
+
} finally {
|
|
903
|
+
cleanup(base);
|
|
904
|
+
}
|
|
905
|
+
});
|
|
906
|
+
|
|
738
907
|
test("hasImplementationArtifacts treats empty non-integration branch diff as absent (#4699)", () => {
|
|
739
908
|
const base = makeGitBase();
|
|
740
909
|
try {
|