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
|
@@ -28,7 +28,7 @@ Then do the thing `STATE.md` says to do next.
|
|
|
28
28
|
## The Hierarchy
|
|
29
29
|
|
|
30
30
|
```
|
|
31
|
-
Milestone → a shippable version (
|
|
31
|
+
Milestone → a shippable version (1-10 slices, sized to the work)
|
|
32
32
|
Slice → one demoable vertical capability (1-7 tasks)
|
|
33
33
|
Task → one context-window-sized unit of work (fits in one session)
|
|
34
34
|
```
|
|
@@ -331,7 +331,7 @@ The **Don't Hand-Roll** and **Common Pitfalls** sections prevent the most expens
|
|
|
331
331
|
|
|
332
332
|
**For a milestone (roadmap):**
|
|
333
333
|
1. Read `M###-CONTEXT.md`, `M###-RESEARCH.md`, and `.gsd/DECISIONS.md` if they exist.
|
|
334
|
-
2. Decompose the vision into
|
|
334
|
+
2. Decompose the vision into 1-10 demoable vertical slices. Prefer one slice for tiny, single-file, or static work unless the request clearly spans independent capabilities.
|
|
335
335
|
3. Order by risk (high-risk first to validate feasibility early).
|
|
336
336
|
4. Write `M###-ROADMAP.md` with checkboxes, risk levels, dependencies, demo sentences.
|
|
337
337
|
5. **Write the boundary map** — for each slice, specify what it produces (functions, types, interfaces, endpoints) and what it consumes from upstream slices. This forces interface thinking before implementation and enables deterministic verification that slices actually connect.
|
|
@@ -11,6 +11,36 @@
|
|
|
11
11
|
|
|
12
12
|
import { buildPrEvidence } from "../gsd/pr-evidence.js";
|
|
13
13
|
|
|
14
|
+
// ─── Helpers ────────────────────────────────────────────────────────────────
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Wrap a string in a CommonMark inline-code span, escaping any embedded
|
|
18
|
+
* backticks by selecting a fence longer than the longest backtick run inside
|
|
19
|
+
* the input. If the input begins or ends with a backtick, pad with a single
|
|
20
|
+
* space inside the fence (CommonMark requirement).
|
|
21
|
+
*
|
|
22
|
+
* Empty input returns an empty string (no fence) — there is nothing to render
|
|
23
|
+
* as code, and emitting an empty pair of backticks would produce literal
|
|
24
|
+
* backticks in GitHub-flavored markdown.
|
|
25
|
+
*/
|
|
26
|
+
export function inlineCode(s: string): string {
|
|
27
|
+
if (s.length === 0) return "";
|
|
28
|
+
let longestRun = 0;
|
|
29
|
+
let currentRun = 0;
|
|
30
|
+
for (const ch of s) {
|
|
31
|
+
if (ch === "`") {
|
|
32
|
+
currentRun++;
|
|
33
|
+
if (currentRun > longestRun) longestRun = currentRun;
|
|
34
|
+
} else {
|
|
35
|
+
currentRun = 0;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
const fence = "`".repeat(longestRun + 1);
|
|
39
|
+
const needsPad = s.startsWith("`") || s.endsWith("`");
|
|
40
|
+
const pad = needsPad ? " " : "";
|
|
41
|
+
return `${fence}${pad}${s}${pad}${fence}`;
|
|
42
|
+
}
|
|
43
|
+
|
|
14
44
|
// ─── Milestone Issue Body ───────────────────────────────────────────────────
|
|
15
45
|
|
|
16
46
|
export interface MilestoneData {
|
|
@@ -124,7 +154,7 @@ export function formatTaskIssueBody(data: TaskData): string {
|
|
|
124
154
|
if (data.files?.length) {
|
|
125
155
|
lines.push("### Files");
|
|
126
156
|
for (const file of data.files) {
|
|
127
|
-
lines.push(`-
|
|
157
|
+
lines.push(`- ${inlineCode(file)}`);
|
|
128
158
|
}
|
|
129
159
|
lines.push("");
|
|
130
160
|
}
|
|
@@ -227,15 +257,14 @@ export function formatSwarmLanePRBody(data: SwarmLanePRData): string {
|
|
|
227
257
|
const summaries = [
|
|
228
258
|
[
|
|
229
259
|
"### Swarm lane",
|
|
230
|
-
`**Lane:**
|
|
231
|
-
`**Branch:**
|
|
260
|
+
`**Lane:** ${inlineCode(laneLabel)}`,
|
|
261
|
+
`**Branch:** ${inlineCode(data.lane.branch)}`,
|
|
232
262
|
data.lane.owner ? `**Owner:** ${data.lane.owner}` : "",
|
|
233
|
-
data.lane.latestCommit ? `**Latest commit:**
|
|
263
|
+
data.lane.latestCommit ? `**Latest commit:** ${inlineCode(data.lane.latestCommit)}` : "",
|
|
234
264
|
].filter(Boolean).join("\n"),
|
|
235
265
|
`### Impact area\n${data.impactArea}`,
|
|
236
266
|
`### Changed contracts\n${checkedList(data.lane.changedContracts, "No shared contracts changed").join("\n")}`,
|
|
237
267
|
`### Transition risks\n${checkedList(data.transitionRisks, "No transition risks identified").join("\n")}`,
|
|
238
|
-
data.lane.blockers?.length ? `### Blockers\n${data.lane.blockers.map((blocker) => `- ${blocker}`).join("\n")}` : "",
|
|
239
268
|
].filter(Boolean);
|
|
240
269
|
|
|
241
270
|
return buildPrEvidence({
|
|
@@ -246,6 +275,7 @@ export function formatSwarmLanePRBody(data: SwarmLanePRData): string {
|
|
|
246
275
|
changeType: "refactor",
|
|
247
276
|
linkedIssue: data.linkedIssue ? `Closes #${data.linkedIssue}` : undefined,
|
|
248
277
|
summaries,
|
|
278
|
+
blockers: data.lane.blockers ?? [],
|
|
249
279
|
testsRun: data.lane.testEvidence,
|
|
250
280
|
rollbackNotes: data.rollbackPlan,
|
|
251
281
|
how: "Generated by GSD GitHub Sync swarm routines from lane evidence.",
|
|
@@ -257,7 +287,7 @@ export function formatSwarmReleaseChecklistBody(data: SwarmReleaseChecklistData)
|
|
|
257
287
|
|
|
258
288
|
lines.push(`# UOK Swarm Release Checklist`);
|
|
259
289
|
lines.push("");
|
|
260
|
-
lines.push(`**Integration branch:**
|
|
290
|
+
lines.push(`**Integration branch:** ${inlineCode(data.integrationBranch)}`);
|
|
261
291
|
lines.push("");
|
|
262
292
|
|
|
263
293
|
lines.push("## Lane summary");
|
|
@@ -266,9 +296,9 @@ export function formatSwarmReleaseChecklistBody(data: SwarmReleaseChecklistData)
|
|
|
266
296
|
lines.push("|------|--------|-------|--------|--------|");
|
|
267
297
|
for (const lane of data.lanes) {
|
|
268
298
|
const owner = lane.owner ?? "";
|
|
269
|
-
const commit = lane.latestCommit ?
|
|
299
|
+
const commit = lane.latestCommit ? inlineCode(lane.latestCommit) : "";
|
|
270
300
|
const status = lane.blockers?.length ? "blocked" : "ready";
|
|
271
|
-
lines.push(`|
|
|
301
|
+
lines.push(`| ${inlineCode(SWARM_LANE_LABELS[lane.id])} | ${inlineCode(lane.branch)} | ${owner} | ${commit} | ${status} |`);
|
|
272
302
|
}
|
|
273
303
|
lines.push("");
|
|
274
304
|
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Project/App: GSD-2
|
|
2
|
+
// File Purpose: Tests for the inlineCode markdown helper and its use in PR body templates.
|
|
3
|
+
|
|
4
|
+
import { describe, it } from "node:test";
|
|
5
|
+
import assert from "node:assert/strict";
|
|
6
|
+
import { inlineCode, formatSwarmLanePRBody } from "../templates.ts";
|
|
7
|
+
|
|
8
|
+
describe("inlineCode", () => {
|
|
9
|
+
it("wraps a plain string in single backticks", () => {
|
|
10
|
+
assert.equal(inlineCode("hello"), "`hello`");
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it("uses a double-backtick fence when the input contains a single backtick", () => {
|
|
14
|
+
const out = inlineCode("a`b");
|
|
15
|
+
assert.equal(out, "``a`b``");
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it("uses a 4-backtick fence when the input contains a run of three backticks", () => {
|
|
19
|
+
const out = inlineCode("x```y");
|
|
20
|
+
assert.equal(out, "````x```y````");
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it("pads with a leading space when the input starts with a backtick", () => {
|
|
24
|
+
const out = inlineCode("`leading");
|
|
25
|
+
// longest run = 1 → fence length 2; leading backtick → pad both sides
|
|
26
|
+
assert.equal(out, "`` `leading ``");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("pads with a trailing space when the input ends with a backtick", () => {
|
|
30
|
+
const out = inlineCode("trailing`");
|
|
31
|
+
assert.equal(out, "`` trailing` ``");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it("returns an empty string for empty input", () => {
|
|
35
|
+
// Documented invariant: empty input renders as nothing rather than as
|
|
36
|
+
// a literal pair of backticks (which GFM would render as the characters
|
|
37
|
+
// themselves, not as code).
|
|
38
|
+
assert.equal(inlineCode(""), "");
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("escapes a branch with embedded backticks inside formatSwarmLanePRBody", () => {
|
|
42
|
+
const malicious = "feature`evil";
|
|
43
|
+
const body = formatSwarmLanePRBody({
|
|
44
|
+
lane: {
|
|
45
|
+
id: "workflow",
|
|
46
|
+
branch: malicious,
|
|
47
|
+
},
|
|
48
|
+
impactArea: "test",
|
|
49
|
+
transitionRisks: [],
|
|
50
|
+
rollbackPlan: [],
|
|
51
|
+
});
|
|
52
|
+
// The branch must appear inside a properly fenced inline-code span,
|
|
53
|
+
// i.e. wrapped in the helper's chosen fence (here a double backtick).
|
|
54
|
+
assert.ok(
|
|
55
|
+
body.includes("``feature`evil``"),
|
|
56
|
+
`expected double-backtick fenced branch, got body:\n${body}`,
|
|
57
|
+
);
|
|
58
|
+
// And there must be no markdown break-out: the substring "evil" should
|
|
59
|
+
// never appear unfenced as a bare word adjacent to a closing single
|
|
60
|
+
// backtick (the unpatched template produced "`feature`evil`").
|
|
61
|
+
assert.ok(
|
|
62
|
+
!body.includes("`feature`evil`\n") && !body.includes("`feature`evil` "),
|
|
63
|
+
`expected no inline-code break-out, got body:\n${body}`,
|
|
64
|
+
);
|
|
65
|
+
});
|
|
66
|
+
});
|
|
@@ -359,6 +359,20 @@ export async function autoLoop(
|
|
|
359
359
|
const prefs = deps.loadEffectiveGSDPreferences()?.preferences;
|
|
360
360
|
const uokFlags = resolveUokFlags(prefs);
|
|
361
361
|
|
|
362
|
+
// ── Check sidecar queue before deriveState ──
|
|
363
|
+
// NOTE: Sidecar dequeue MUST run before validateWorkflowSessionLock so a
|
|
364
|
+
// queued item is popped (and the `sidecar-dequeue` journal event emitted)
|
|
365
|
+
// even when the session lock invalidates this iteration. Inverting this
|
|
366
|
+
// order silently drops queued items on lock-loss. Refs #5308.
|
|
367
|
+
const sidecarItem = await dequeueSidecarItem({
|
|
368
|
+
queue: s.sidecarQueue,
|
|
369
|
+
executionGraphEnabled: uokFlags.executionGraph,
|
|
370
|
+
scheduleQueue: scheduleSidecarQueue,
|
|
371
|
+
warnSchedulingFailure: message => logWarning("dispatch", `sidecar queue scheduling failed: ${message}`),
|
|
372
|
+
logDequeue: payload => debugLog("autoLoop", { phase: "sidecar-dequeue", ...payload }),
|
|
373
|
+
emitDequeue: payload => journalReporter.emit("sidecar-dequeue", payload),
|
|
374
|
+
});
|
|
375
|
+
|
|
362
376
|
const sessionLockOutcome = validateWorkflowSessionLock({
|
|
363
377
|
active: s.active,
|
|
364
378
|
iteration,
|
|
@@ -378,19 +392,10 @@ export async function autoLoop(
|
|
|
378
392
|
},
|
|
379
393
|
});
|
|
380
394
|
if (sessionLockOutcome.action === "stop" && sessionLockOutcome.reason === "session-lock-lost") {
|
|
395
|
+
finishTurn("stopped", "manual-attention", sessionLockOutcome.reason);
|
|
381
396
|
break;
|
|
382
397
|
}
|
|
383
398
|
|
|
384
|
-
// ── Check sidecar queue before deriveState ──
|
|
385
|
-
const sidecarItem = await dequeueSidecarItem({
|
|
386
|
-
queue: s.sidecarQueue,
|
|
387
|
-
executionGraphEnabled: uokFlags.executionGraph,
|
|
388
|
-
scheduleQueue: scheduleSidecarQueue,
|
|
389
|
-
warnSchedulingFailure: message => logWarning("dispatch", `sidecar queue scheduling failed: ${message}`),
|
|
390
|
-
logDequeue: payload => debugLog("autoLoop", { phase: "sidecar-dequeue", ...payload }),
|
|
391
|
-
emitDequeue: payload => journalReporter.emit("sidecar-dequeue", payload),
|
|
392
|
-
});
|
|
393
|
-
|
|
394
399
|
const ic: IterationContext = { ctx, pi, s, deps, prefs, iteration, flowId, nextSeq };
|
|
395
400
|
journalReporter.emit("iteration-start", { iteration });
|
|
396
401
|
let iterData: IterationData;
|
|
@@ -427,6 +432,7 @@ export async function autoLoop(
|
|
|
427
432
|
isComplete: engineState.isComplete,
|
|
428
433
|
});
|
|
429
434
|
if (engineState.isComplete) {
|
|
435
|
+
finishTurn("completed");
|
|
430
436
|
await deps.stopAuto(ctx, pi, "Workflow complete");
|
|
431
437
|
break;
|
|
432
438
|
}
|
|
@@ -873,6 +879,7 @@ export async function autoLoop(
|
|
|
873
879
|
|
|
874
880
|
if (cooldownDecision.action === "stop") {
|
|
875
881
|
ctx.ui.notify(cooldownDecision.notifyMessage, "error");
|
|
882
|
+
finishTurn("stopped", "timeout", msg);
|
|
876
883
|
await deps.stopAuto(ctx, pi, cooldownDecision.stopMessage);
|
|
877
884
|
break;
|
|
878
885
|
}
|
|
@@ -32,13 +32,13 @@ import { detectStuck } from "./detect-stuck.js";
|
|
|
32
32
|
import { runUnit } from "./run-unit.js";
|
|
33
33
|
import { debugLog } from "../debug-logger.js";
|
|
34
34
|
import { resolveWorktreeProjectRoot, normalizeWorktreePathForCompare } from "../worktree-root.js";
|
|
35
|
-
import {
|
|
35
|
+
import { classifyProject } from "../detection.js";
|
|
36
36
|
import { MergeConflictError } from "../git-service.js";
|
|
37
37
|
import { setCurrentPhase, clearCurrentPhase } from "../../shared/gsd-phase-state.js";
|
|
38
38
|
import { pauseAutoForProviderError } from "../provider-error-pause.js";
|
|
39
39
|
import { resumeAutoAfterProviderDelay } from "../bootstrap/provider-error-resume.js";
|
|
40
40
|
import { join, basename } from "node:path";
|
|
41
|
-
import { existsSync, cpSync
|
|
41
|
+
import { existsSync, cpSync } from "node:fs";
|
|
42
42
|
import {
|
|
43
43
|
logWarning,
|
|
44
44
|
logError,
|
|
@@ -684,6 +684,7 @@ export async function runPreDispatch(
|
|
|
684
684
|
deps.postflightPopStash(
|
|
685
685
|
s.originalBasePath || s.basePath,
|
|
686
686
|
s.currentMilestoneId!,
|
|
687
|
+
preflightTransition.stashMarker,
|
|
687
688
|
ctx.ui.notify.bind(ctx.ui),
|
|
688
689
|
);
|
|
689
690
|
}
|
|
@@ -797,6 +798,7 @@ export async function runPreDispatch(
|
|
|
797
798
|
deps.postflightPopStash(
|
|
798
799
|
s.originalBasePath || s.basePath,
|
|
799
800
|
s.currentMilestoneId,
|
|
801
|
+
preflightAllComplete.stashMarker,
|
|
800
802
|
ctx.ui.notify.bind(ctx.ui),
|
|
801
803
|
);
|
|
802
804
|
}
|
|
@@ -925,6 +927,7 @@ export async function runPreDispatch(
|
|
|
925
927
|
deps.postflightPopStash(
|
|
926
928
|
s.originalBasePath || s.basePath,
|
|
927
929
|
s.currentMilestoneId,
|
|
930
|
+
preflightComplete.stashMarker,
|
|
928
931
|
ctx.ui.notify.bind(ctx.ui),
|
|
929
932
|
);
|
|
930
933
|
}
|
|
@@ -1484,8 +1487,9 @@ export async function runUnitPhase(
|
|
|
1484
1487
|
// Verify the working directory is a valid git checkout with project
|
|
1485
1488
|
// files before dispatching work. A broken worktree causes agents to
|
|
1486
1489
|
// hallucinate summaries since they cannot read or write any files.
|
|
1487
|
-
// Uses
|
|
1488
|
-
//
|
|
1490
|
+
// Uses project classification so project presence is not conflated with
|
|
1491
|
+
// ecosystem marker detection. Static/minimal repos become untyped-existing.
|
|
1492
|
+
let projectClassification: ReturnType<typeof classifyProject> | null = null;
|
|
1489
1493
|
if (s.basePath && unitType === "execute-task") {
|
|
1490
1494
|
const gitMarker = join(s.basePath, ".git");
|
|
1491
1495
|
const hasGit = deps.existsSync(gitMarker);
|
|
@@ -1496,30 +1500,29 @@ export async function runUnitPhase(
|
|
|
1496
1500
|
await deps.stopAuto(ctx, pi, msg);
|
|
1497
1501
|
return { action: "break", reason: "worktree-invalid" };
|
|
1498
1502
|
}
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
ctx.ui.notify(`Warning: ${s.basePath} has no recognized project files — proceeding as greenfield project`, "warning");
|
|
1503
|
+
projectClassification = classifyProject(s.basePath);
|
|
1504
|
+
if (projectClassification.kind === "invalid-repo") {
|
|
1505
|
+
const msg = `Worktree health check failed: ${s.basePath} classified as invalid-repo (${projectClassification.reason}) — refusing to dispatch ${unitType} ${unitId}`;
|
|
1506
|
+
debugLog("runUnitPhase", { phase: "worktree-health-invalid-repo", basePath: s.basePath, classification: projectClassification });
|
|
1507
|
+
if (projectClassification.reason === "missing .git" && hasGit) {
|
|
1508
|
+
ctx.ui.notify(
|
|
1509
|
+
`Warning: ${s.basePath} project classification could not confirm .git; assuming it has no project content yet — proceeding as greenfield project because worktree health reported .git present`,
|
|
1510
|
+
"warning",
|
|
1511
|
+
);
|
|
1512
|
+
} else {
|
|
1513
|
+
ctx.ui.notify(msg, "error");
|
|
1514
|
+
await deps.stopAuto(ctx, pi, msg);
|
|
1515
|
+
return { action: "break", reason: "worktree-invalid" };
|
|
1516
|
+
}
|
|
1517
|
+
} else if (projectClassification.kind === "greenfield") {
|
|
1518
|
+
debugLog("runUnitPhase", { phase: "worktree-health-greenfield", basePath: s.basePath, classification: projectClassification });
|
|
1519
|
+
ctx.ui.notify(`Warning: ${s.basePath} has no project content yet — proceeding as greenfield project`, "warning");
|
|
1520
|
+
} else if (projectClassification.kind === "untyped-existing") {
|
|
1521
|
+
debugLog("runUnitPhase", { phase: "worktree-health-untyped-existing", basePath: s.basePath, classification: projectClassification });
|
|
1522
|
+
ctx.ui.notify(
|
|
1523
|
+
`Notice: ${s.basePath} has existing project content but no recognized tooling markers — using generic file-level workflow guidance`,
|
|
1524
|
+
"info",
|
|
1525
|
+
);
|
|
1523
1526
|
}
|
|
1524
1527
|
}
|
|
1525
1528
|
|
|
@@ -1598,6 +1601,17 @@ export async function runUnitPhase(
|
|
|
1598
1601
|
// Prompt injection
|
|
1599
1602
|
let finalPrompt = prompt;
|
|
1600
1603
|
|
|
1604
|
+
if (unitType === "execute-task") {
|
|
1605
|
+
projectClassification ??= classifyProject(s.basePath);
|
|
1606
|
+
if (projectClassification.kind === "untyped-existing") {
|
|
1607
|
+
const samples = projectClassification.contentFiles.slice(0, 8).join(", ") || "project files";
|
|
1608
|
+
finalPrompt +=
|
|
1609
|
+
"\n\n**Project classification:** Existing untyped project. No recognized build/tooling markers were detected, " +
|
|
1610
|
+
"so use generic file-level workflow guidance. Task plans and completion summaries must list every concrete " +
|
|
1611
|
+
`project file changed in \`files\` or \`expected_output\`. Detected content sample: ${samples}.`;
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
|
|
1601
1615
|
if (s.pendingVerificationRetry) {
|
|
1602
1616
|
const retryCtx = s.pendingVerificationRetry;
|
|
1603
1617
|
s.pendingVerificationRetry = null;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// GSD-2 + src/resources/extensions/gsd/auto/run-unit.ts - Runs one GSD auto-mode unit from session creation through agent completion.
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* auto/run-unit.ts — Single unit execution: session create → prompt → await agent_end.
|
|
3
5
|
*
|
|
@@ -17,6 +19,7 @@ import {
|
|
|
17
19
|
import { debugLog } from "../debug-logger.js";
|
|
18
20
|
import { logWarning, logError } from "../workflow-logger.js";
|
|
19
21
|
import { resolveAutoSupervisorConfig } from "../preferences.js";
|
|
22
|
+
import { formatAutoUnitWorkingMessage } from "../working-output-messages.js";
|
|
20
23
|
|
|
21
24
|
// Tracks the latest session-switch attempt so a late timeout settlement from an
|
|
22
25
|
// older runUnit() call cannot clear the guard for a newer one.
|
|
@@ -184,30 +187,37 @@ export async function runUnit(
|
|
|
184
187
|
debugLog("runUnit", { phase: "send-message", unitType, unitId });
|
|
185
188
|
|
|
186
189
|
const requestDispatchedAt = Date.now();
|
|
187
|
-
|
|
188
|
-
{ customType: "gsd-auto", content: prompt, display: s.verbose },
|
|
189
|
-
{ triggerTurn: true },
|
|
190
|
-
);
|
|
190
|
+
ctx.ui.setWorkingMessage?.(formatAutoUnitWorkingMessage(unitType, unitId));
|
|
191
191
|
|
|
192
192
|
// ── Await agent_end with absolute timeout (H4 fix) ──
|
|
193
193
|
// If supervision fails to resolve unitPromise within 30s, treat as cancelled.
|
|
194
194
|
// Without this, a crashed agent that never emits agent_end hangs the loop (#3161).
|
|
195
|
-
debugLog("runUnit", { phase: "awaiting-agent-end", unitType, unitId });
|
|
196
195
|
const supervisor = resolveAutoSupervisorConfig();
|
|
197
196
|
const UNIT_HARD_TIMEOUT_MS = Math.max(
|
|
198
197
|
30_000,
|
|
199
198
|
((supervisor.hard_timeout_minutes ?? 30) * 60 * 1000) + 30_000,
|
|
200
199
|
);
|
|
201
200
|
let unitTimeoutHandle: ReturnType<typeof setTimeout> | undefined;
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
201
|
+
let result: UnitResult;
|
|
202
|
+
try {
|
|
203
|
+
pi.sendMessage(
|
|
204
|
+
{ customType: "gsd-auto", content: prompt, display: s.verbose },
|
|
205
|
+
{ triggerTurn: true },
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
debugLog("runUnit", { phase: "awaiting-agent-end", unitType, unitId });
|
|
209
|
+
const timeoutResult = new Promise<UnitResult>((resolve) => {
|
|
210
|
+
unitTimeoutHandle = setTimeout(() => {
|
|
211
|
+
resolve({ status: "cancelled", errorContext: { message: "Unit hard timeout — supervision may have failed", category: "timeout", isTransient: true } });
|
|
212
|
+
}, UNIT_HARD_TIMEOUT_MS);
|
|
213
|
+
});
|
|
214
|
+
result = await runWithTurnGeneration(capturedTurnGen, () =>
|
|
215
|
+
Promise.race([unitPromise, timeoutResult]),
|
|
216
|
+
);
|
|
217
|
+
} finally {
|
|
218
|
+
if (unitTimeoutHandle) clearTimeout(unitTimeoutHandle);
|
|
219
|
+
ctx.ui.setWorkingMessage?.(undefined);
|
|
220
|
+
}
|
|
211
221
|
debugLog("runUnit", {
|
|
212
222
|
phase: "agent-end-received",
|
|
213
223
|
unitType,
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
// GSD-2 + src/resources/extensions/gsd/auto-dashboard.ts - Auto-mode progress widget rendering and dashboard helpers.
|
|
2
|
+
|
|
1
3
|
/**
|
|
2
4
|
* Auto-mode Dashboard — progress widget rendering, elapsed time formatting,
|
|
3
5
|
* unit description helpers, and slice progress caching.
|
|
@@ -46,6 +48,7 @@ import {
|
|
|
46
48
|
import { logWarning } from "./workflow-logger.js";
|
|
47
49
|
import { formattedShortcutPair } from "./shortcut-defs.js";
|
|
48
50
|
import { homedir } from "node:os";
|
|
51
|
+
import { readUnitRuntimeRecord, type AutoUnitRuntimeRecord } from "./unit-runtime.js";
|
|
49
52
|
|
|
50
53
|
// ─── UAT Slice Extraction ─────────────────────────────────────────────────────
|
|
51
54
|
|
|
@@ -215,6 +218,36 @@ export function formatWidgetTokens(count: number): string {
|
|
|
215
218
|
return `${Math.round(count / 1000000)}M`;
|
|
216
219
|
}
|
|
217
220
|
|
|
221
|
+
export function formatRuntimeHealthSignal(
|
|
222
|
+
record: AutoUnitRuntimeRecord | null,
|
|
223
|
+
now = Date.now(),
|
|
224
|
+
): { level: "green" | "yellow"; summary: string; detail?: string } | null {
|
|
225
|
+
if (!record) return null;
|
|
226
|
+
const idleMs = Math.max(0, now - record.lastProgressAt);
|
|
227
|
+
const idleMinutes = Math.floor(idleMs / 60_000);
|
|
228
|
+
if ((record.recoveryAttempts ?? 0) > 0 || record.phase === "recovered" || record.lastProgressKind.includes("recovery")) {
|
|
229
|
+
return {
|
|
230
|
+
level: "yellow",
|
|
231
|
+
summary: "Recovering",
|
|
232
|
+
detail: `retry ${record.recoveryAttempts ?? 1} after ${record.lastRecoveryReason ?? "idle"} stall`,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
if (record.progressCount === 0 && idleMs >= 60_000) {
|
|
236
|
+
return {
|
|
237
|
+
level: "yellow",
|
|
238
|
+
summary: "Waiting on provider",
|
|
239
|
+
detail: `no output for ${idleMinutes}m`,
|
|
240
|
+
};
|
|
241
|
+
}
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export function shouldRenderRoadmapProgress(
|
|
246
|
+
progress: { total: number; activeSliceTasks?: { total: number } | null } | null,
|
|
247
|
+
): progress is { total: number; activeSliceTasks?: { total: number } | null } {
|
|
248
|
+
return !!progress && progress.total > 0;
|
|
249
|
+
}
|
|
250
|
+
|
|
218
251
|
// ─── ETA Estimation ──────────────────────────────────────────────────────────
|
|
219
252
|
|
|
220
253
|
/**
|
|
@@ -622,6 +655,7 @@ export function updateProgressWidget(
|
|
|
622
655
|
let cachedLines: string[] | undefined;
|
|
623
656
|
let cachedWidth: number | undefined;
|
|
624
657
|
let cachedRtkLabel: string | null | undefined;
|
|
658
|
+
let cachedRuntimeRecord: AutoUnitRuntimeRecord | null = null;
|
|
625
659
|
|
|
626
660
|
const refreshRtkLabel = (): void => {
|
|
627
661
|
try {
|
|
@@ -634,7 +668,16 @@ export function updateProgressWidget(
|
|
|
634
668
|
}
|
|
635
669
|
};
|
|
636
670
|
|
|
671
|
+
const refreshRuntimeRecord = (): void => {
|
|
672
|
+
try {
|
|
673
|
+
cachedRuntimeRecord = readUnitRuntimeRecord(accessors.getBasePath(), unitType, unitId);
|
|
674
|
+
} catch {
|
|
675
|
+
cachedRuntimeRecord = null;
|
|
676
|
+
}
|
|
677
|
+
};
|
|
678
|
+
|
|
637
679
|
refreshRtkLabel();
|
|
680
|
+
refreshRuntimeRecord();
|
|
638
681
|
|
|
639
682
|
const pulseTimer = setInterval(() => {
|
|
640
683
|
pulseBright = !pulseBright;
|
|
@@ -652,6 +695,7 @@ export function updateProgressWidget(
|
|
|
652
695
|
updateSliceProgressCache(accessors.getBasePath(), mid.id, slice?.id);
|
|
653
696
|
}
|
|
654
697
|
refreshRtkLabel();
|
|
698
|
+
refreshRuntimeRecord();
|
|
655
699
|
cachedLines = undefined;
|
|
656
700
|
} catch (err) { /* non-fatal */
|
|
657
701
|
logWarning("dashboard", `DB status update failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
@@ -685,13 +729,16 @@ export function updateProgressWidget(
|
|
|
685
729
|
|
|
686
730
|
// Health indicator in header
|
|
687
731
|
const score = computeProgressScore();
|
|
688
|
-
const
|
|
689
|
-
|
|
732
|
+
const runtimeSignal = formatRuntimeHealthSignal(cachedRuntimeRecord);
|
|
733
|
+
const healthLevel = runtimeSignal?.level ?? score.level;
|
|
734
|
+
const healthSummary = runtimeSignal?.summary ?? score.summary;
|
|
735
|
+
const healthColor = healthLevel === "green" ? "success"
|
|
736
|
+
: healthLevel === "yellow" ? "warning"
|
|
690
737
|
: "error";
|
|
691
|
-
const healthIcon =
|
|
692
|
-
:
|
|
738
|
+
const healthIcon = healthLevel === "green" ? GLYPH.statusActive
|
|
739
|
+
: healthLevel === "yellow" ? "!"
|
|
693
740
|
: "x";
|
|
694
|
-
const healthStr = ` ${theme.fg(healthColor, healthIcon)} ${theme.fg(healthColor,
|
|
741
|
+
const healthStr = ` ${theme.fg(healthColor, healthIcon)} ${theme.fg(healthColor, healthSummary)}`;
|
|
695
742
|
|
|
696
743
|
const headerLeft = `${pad}${dot} ${theme.fg("accent", theme.bold("GSD"))} ${theme.fg("success", modeTag)}${healthStr}`;
|
|
697
744
|
|
|
@@ -706,7 +753,9 @@ export function updateProgressWidget(
|
|
|
706
753
|
lines.push(rightAlign(headerLeft, headerRight, width));
|
|
707
754
|
|
|
708
755
|
// Show health signal details when degraded (yellow/red)
|
|
709
|
-
if (
|
|
756
|
+
if (runtimeSignal?.detail && widgetMode !== "min") {
|
|
757
|
+
lines.push(`${pad} ${theme.fg("dim", runtimeSignal.detail)}`);
|
|
758
|
+
} else if (score.level !== "green" && score.signals.length > 0 && widgetMode !== "min") {
|
|
710
759
|
// Show up to 3 most relevant signals in compact form
|
|
711
760
|
const topSignals = score.signals
|
|
712
761
|
.filter(s => s.kind === "negative")
|
|
@@ -785,7 +834,7 @@ export function updateProgressWidget(
|
|
|
785
834
|
|
|
786
835
|
// Progress bar
|
|
787
836
|
const roadmapSlices = mid ? getRoadmapSlicesSync() : null;
|
|
788
|
-
if (roadmapSlices) {
|
|
837
|
+
if (shouldRenderRoadmapProgress(roadmapSlices)) {
|
|
789
838
|
const { done, total, activeSliceTasks } = roadmapSlices;
|
|
790
839
|
const barWidth = Math.max(6, Math.min(18, Math.floor(width * 0.25)));
|
|
791
840
|
const pct = total > 0 ? done / total : 0;
|
|
@@ -853,7 +902,7 @@ export function updateProgressWidget(
|
|
|
853
902
|
|
|
854
903
|
const leftLines: string[] = [];
|
|
855
904
|
|
|
856
|
-
if (roadmapSlices) {
|
|
905
|
+
if (shouldRenderRoadmapProgress(roadmapSlices)) {
|
|
857
906
|
const { done, total, activeSliceTasks } = roadmapSlices;
|
|
858
907
|
const barWidth = Math.max(6, Math.min(18, Math.floor(leftColWidth * 0.4)));
|
|
859
908
|
const pct = total > 0 ? done / total : 0;
|
|
@@ -211,6 +211,23 @@ export function hasPendingDeepStage(prefs: GSDPreferences | undefined, basePath:
|
|
|
211
211
|
return gate.status === "pending" || gate.status === "blocked";
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
+
export function shouldRunDeepProjectSetup(
|
|
215
|
+
state: Pick<GSDState, "phase">,
|
|
216
|
+
prefs: GSDPreferences | undefined,
|
|
217
|
+
basePath: string,
|
|
218
|
+
options: { hasSurvivorBranch?: boolean } = {},
|
|
219
|
+
): boolean {
|
|
220
|
+
if (options.hasSurvivorBranch === true) return false;
|
|
221
|
+
if (
|
|
222
|
+
state.phase !== "pre-planning" &&
|
|
223
|
+
state.phase !== "needs-discussion" &&
|
|
224
|
+
state.phase !== "planning"
|
|
225
|
+
) {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
return hasPendingDeepStage(prefs, basePath);
|
|
229
|
+
}
|
|
230
|
+
|
|
214
231
|
function missingSliceStop(mid: string, phase: string): DispatchAction {
|
|
215
232
|
return {
|
|
216
233
|
action: "stop",
|
|
@@ -43,7 +43,7 @@ import {
|
|
|
43
43
|
import { regenerateIfMissing } from "./workflow-projections.js";
|
|
44
44
|
import { syncStateToProjectRoot } from "./auto-worktree.js";
|
|
45
45
|
import { normalizeWorktreePathForCompare } from "./worktree-root.js";
|
|
46
|
-
import { isDbAvailable, getTask, getSlice, getMilestone, updateTaskStatus, _getAdapter } from "./gsd-db.js";
|
|
46
|
+
import { isDbAvailable, getTask, getSlice, getMilestone, updateTaskStatus, _getAdapter, getVerificationEvidence } from "./gsd-db.js";
|
|
47
47
|
import { renderPlanCheckboxes } from "./markdown-renderer.js";
|
|
48
48
|
import { consumeSignal } from "./session-status-io.js";
|
|
49
49
|
import {
|
|
@@ -852,22 +852,22 @@ export async function postUnitPreVerification(pctx: PostUnitContext, opts?: PreV
|
|
|
852
852
|
}
|
|
853
853
|
|
|
854
854
|
// Evidence cross-reference (execute-task only)
|
|
855
|
-
//
|
|
856
|
-
//
|
|
857
|
-
//
|
|
858
|
-
// we can still detect units that claimed success but ran no commands.
|
|
855
|
+
// Only compare against concrete command evidence persisted by the task
|
|
856
|
+
// completion tool. A prose Verify field can be satisfied later by the
|
|
857
|
+
// host verification gate, so it is not enough to accuse the unit.
|
|
859
858
|
if (safetyConfig.evidence_cross_reference && s.currentUnit.type === "execute-task") {
|
|
860
859
|
try {
|
|
861
860
|
const actual = getEvidence();
|
|
862
861
|
const bashCalls = actual.filter(e => e.kind === "bash");
|
|
863
|
-
// If the task is marked complete but zero bash commands were run,
|
|
864
|
-
// it's suspicious — the LLM may have fabricated results.
|
|
865
862
|
if (sMid && sSid && sTid && isDbAvailable()) {
|
|
866
863
|
const taskRow = getTask(sMid, sSid, sTid);
|
|
867
|
-
|
|
868
|
-
|
|
864
|
+
const claimedCommands = getVerificationEvidence(sMid, sSid, sTid)
|
|
865
|
+
.map((row) => row.command)
|
|
866
|
+
.filter((command): command is string => typeof command === "string" && command.trim().length > 0);
|
|
867
|
+
if (taskRow?.status === "complete" && claimedCommands.length > 0 && bashCalls.length === 0) {
|
|
868
|
+
logWarning("safety", "task claimed verification command evidence but no execution tool calls were recorded");
|
|
869
869
|
ctx.ui.notify(
|
|
870
|
-
`Safety: task ${sTid}
|
|
870
|
+
`Safety: task ${sTid} claimed command evidence but no execution tool calls were recorded`,
|
|
871
871
|
"warning",
|
|
872
872
|
);
|
|
873
873
|
}
|