@opengsd/gsd-pi 1.8.0 → 1.8.1-dev.897b3c90
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -3
- package/dist/cli.js +5 -1
- package/dist/headless-events.d.ts +2 -1
- package/dist/headless-events.js +11 -3
- package/dist/headless-milestone-readiness.d.ts +29 -0
- package/dist/headless-milestone-readiness.js +161 -0
- package/dist/headless.js +25 -2
- package/dist/print-mode-exit.d.ts +12 -0
- package/dist/print-mode-exit.js +14 -0
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/GSD-WORKFLOW.md +16 -47
- package/dist/resources/extensions/browser-tools/capture.js +2 -77
- package/dist/resources/extensions/browser-tools/screenshot-constraints.js +83 -0
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +17 -2
- package/dist/resources/extensions/claude-code-cli/turn-assembler.js +30 -2
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +2 -2
- package/dist/resources/extensions/gsd/auto-dispatch.js +74 -19
- package/dist/resources/extensions/gsd/auto-post-unit.js +4 -0
- package/dist/resources/extensions/gsd/auto-prompts.js +35 -1
- package/dist/resources/extensions/gsd/auto.js +5 -1
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +78 -4
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +84 -26
- package/dist/resources/extensions/gsd/command-feedback.js +16 -12
- package/dist/resources/extensions/gsd/compat/compat-marker.js +19 -2
- package/dist/resources/extensions/gsd/db/command-queue.js +7 -2
- package/dist/resources/extensions/gsd/db/engine.js +51 -16
- package/dist/resources/extensions/gsd/db/writers/cascades.js +5 -1
- package/dist/resources/extensions/gsd/db/writers/status.js +13 -3
- package/dist/resources/extensions/gsd/db-base-schema.js +28 -0
- package/dist/resources/extensions/gsd/db-migration-steps.js +30 -0
- package/dist/resources/extensions/gsd/db-schema-metadata.js +4 -1
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +4 -4
- package/dist/resources/extensions/gsd/doctor-history.js +65 -0
- package/dist/resources/extensions/gsd/doctor-state-checks.js +509 -0
- package/dist/resources/extensions/gsd/doctor-workspace-checks.js +91 -0
- package/dist/resources/extensions/gsd/doctor.js +14 -653
- package/dist/resources/extensions/gsd/flat-phase-migration.js +156 -21
- package/dist/resources/extensions/gsd/gsd-db.js +187 -45
- package/dist/resources/extensions/gsd/guided-flow.js +29 -10
- package/dist/resources/extensions/gsd/markdown-renderer.js +16 -2
- package/dist/resources/extensions/gsd/md-importer.js +109 -16
- package/dist/resources/extensions/gsd/migrate/command.js +19 -162
- package/dist/resources/extensions/gsd/migrate/execution.js +98 -0
- package/dist/resources/extensions/gsd/migrate/plan.js +49 -0
- package/dist/resources/extensions/gsd/migrate/planning-writer.js +20 -19
- package/dist/resources/extensions/gsd/migrate/presentation.js +64 -0
- package/dist/resources/extensions/gsd/migration-auto-check.js +4 -0
- package/dist/resources/extensions/gsd/parallel-eligibility.js +5 -4
- package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +28 -43
- package/dist/resources/extensions/gsd/paths.js +4 -1
- package/dist/resources/extensions/gsd/phase-anchor.js +7 -2
- package/dist/resources/extensions/gsd/post-execution-checks.js +4 -4
- package/dist/resources/extensions/gsd/prompts/refine-slice.md +2 -2
- package/dist/resources/extensions/gsd/safety/evidence-collector.js +26 -1
- package/dist/resources/extensions/gsd/safety/evidence-cross-ref.js +30 -5
- package/dist/resources/extensions/gsd/state-reconciliation/drift/external-markdown-edit.js +27 -13
- package/dist/resources/extensions/gsd/state-reconciliation/drift/external-planning-edit.js +13 -3
- package/dist/resources/extensions/gsd/state-reconciliation/drift/project-md.js +54 -14
- package/dist/resources/extensions/gsd/state-reconciliation/drift/sketch-flag.js +48 -5
- package/dist/resources/extensions/gsd/stop-notice.js +15 -1
- package/dist/resources/extensions/gsd/sync-lock.js +13 -7
- package/dist/resources/extensions/gsd/tools/complete-task.js +82 -1
- package/dist/resources/extensions/gsd/tools/plan-task.js +13 -15
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +18 -15
- package/dist/resources/extensions/gsd/tools/replan-task.js +128 -0
- package/dist/resources/extensions/gsd/tools/rework-brief.js +74 -0
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +81 -0
- package/dist/resources/extensions/gsd/unit-registry.js +4 -0
- package/dist/resources/extensions/gsd/uok/plan-v2.js +19 -3
- package/dist/resources/extensions/gsd/worktree-state-projection.js +6 -0
- package/dist/resources/extensions/mac-tools/index.js +31 -9
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +5 -5
- 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/required-server-files.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page.js +3 -3
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +4 -4
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +4 -4
- 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 +4 -4
- 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 +2 -2
- package/dist/web/standalone/.next/server/app/api/boot/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/boot/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/browse-directories/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/captures/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/cleanup/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/dev-mode/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/doctor/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/experimental/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/experimental/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/export-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/forensics/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/history/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/hooks/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/inspect/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/knowledge/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/live-state/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/mcp-connections/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/mcp-connections/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/notifications/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/notifications/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/onboarding/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/preferences/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/projects/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/recovery/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/remote-questions/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/remote-questions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/browser/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/command/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/events/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/session/events/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/session/manage/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/settings-data/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/shutdown/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/skill-health/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/steer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/switch-root/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/input/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route.js +2 -2
- package/dist/web/standalone/.next/server/app/api/terminal/resize/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/sessions/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js +4 -4
- package/dist/web/standalone/.next/server/app/api/terminal/stream/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/terminal/upload/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/undo/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route.js +1 -1
- package/dist/web/standalone/.next/server/app/api/visualizer/route_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +5 -5
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +5 -5
- 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 +4 -4
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/page.js +2 -2
- package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +5 -5
- package/dist/web/standalone/.next/server/chunks/2842.js +4 -4
- package/dist/web/standalone/.next/server/chunks/8357.js +3 -3
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware.js +3 -3
- 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/app/_not-found/page-a6fb1847f67f167c.js +1 -0
- package/dist/web/standalone/.next/static/chunks/app/{layout-b35cbfff38aaf4cf.js → layout-4ae2d68984392bbf.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/app/page-72a856634ad14c10.js +1 -0
- package/dist/web/standalone/.next/static/chunks/{main-app-590a74400e35f685.js → main-app-90d1d8d5e5d2dc6b.js} +1 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-1115d46ac61b9823.js +1 -0
- package/dist/web/standalone/.next/static/css/3b1ceb7abfb9a230.css +1 -0
- package/dist/web/standalone/node_modules/@gsd/native/package.json +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +2 -2
- package/dist/web/standalone/node_modules/node-pty/build/Release/pty.node +0 -0
- package/dist/web/standalone/node_modules/node-pty/build/pty.target.mk +14 -14
- package/dist/web/standalone/package.json +0 -1
- package/dist/web/standalone/server.js +1 -1
- package/dist/web-mode.js +31 -3
- package/integrations/hermes/open_gsd_hermes/gsd_client.py +1 -1
- package/integrations/hermes/pyproject.toml +1 -1
- package/package.json +12 -10
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/dist/workflow.d.ts +14 -0
- package/packages/contracts/dist/workflow.d.ts.map +1 -1
- package/packages/contracts/dist/workflow.js +16 -0
- package/packages/contracts/dist/workflow.js.map +1 -1
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/dist/cloud-config.d.ts +1 -1
- package/packages/daemon/dist/cloud-config.d.ts.map +1 -1
- package/packages/daemon/dist/cloud-config.js +5 -1
- package/packages/daemon/dist/cloud-config.js.map +1 -1
- package/packages/daemon/dist/cloud-config.test.js +51 -1
- package/packages/daemon/dist/cloud-config.test.js.map +1 -1
- package/packages/daemon/dist/local-tool-executor.d.ts.map +1 -1
- package/packages/daemon/dist/local-tool-executor.js +2 -0
- package/packages/daemon/dist/local-tool-executor.js.map +1 -1
- package/packages/daemon/package.json +6 -6
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/gsd-cloud/dist/cli.d.ts +5 -0
- package/packages/gsd-cloud/dist/cli.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/cli.js +160 -0
- package/packages/gsd-cloud/dist/cli.js.map +1 -0
- package/packages/gsd-cloud/dist/cloud-config.d.ts +19 -0
- package/packages/gsd-cloud/dist/cloud-config.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/cloud-config.js +231 -0
- package/packages/gsd-cloud/dist/cloud-config.js.map +1 -0
- package/packages/gsd-cloud/dist/cloud-config.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/cloud-config.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/cloud-config.test.js +118 -0
- package/packages/gsd-cloud/dist/cloud-config.test.js.map +1 -0
- package/packages/gsd-cloud/dist/cloud-gateway-lookup.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/cloud-gateway-lookup.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/cloud-gateway-lookup.test.js +37 -0
- package/packages/gsd-cloud/dist/cloud-gateway-lookup.test.js.map +1 -0
- package/packages/gsd-cloud/dist/cloud-runtime.d.ts +35 -0
- package/packages/gsd-cloud/dist/cloud-runtime.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/cloud-runtime.js +258 -0
- package/packages/gsd-cloud/dist/cloud-runtime.js.map +1 -0
- package/packages/gsd-cloud/dist/cloud-runtime.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/cloud-runtime.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/cloud-runtime.test.js +88 -0
- package/packages/gsd-cloud/dist/cloud-runtime.test.js.map +1 -0
- package/packages/gsd-cloud/dist/cloud-token.d.ts +3 -0
- package/packages/gsd-cloud/dist/cloud-token.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/cloud-token.js +37 -0
- package/packages/gsd-cloud/dist/cloud-token.js.map +1 -0
- package/packages/gsd-cloud/dist/config.d.ts +17 -0
- package/packages/gsd-cloud/dist/config.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/config.js +146 -0
- package/packages/gsd-cloud/dist/config.js.map +1 -0
- package/packages/gsd-cloud/dist/device-flow.d.ts +24 -0
- package/packages/gsd-cloud/dist/device-flow.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/device-flow.js +128 -0
- package/packages/gsd-cloud/dist/device-flow.js.map +1 -0
- package/packages/gsd-cloud/dist/device-flow.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/device-flow.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/device-flow.test.js +155 -0
- package/packages/gsd-cloud/dist/device-flow.test.js.map +1 -0
- package/packages/gsd-cloud/dist/executors/claude-executor.d.ts +6 -0
- package/packages/gsd-cloud/dist/executors/claude-executor.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/executors/claude-executor.js +15 -0
- package/packages/gsd-cloud/dist/executors/claude-executor.js.map +1 -0
- package/packages/gsd-cloud/dist/executors/codex-executor.d.ts +6 -0
- package/packages/gsd-cloud/dist/executors/codex-executor.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/executors/codex-executor.js +15 -0
- package/packages/gsd-cloud/dist/executors/codex-executor.js.map +1 -0
- package/packages/gsd-cloud/dist/executors/executor.d.ts +28 -0
- package/packages/gsd-cloud/dist/executors/executor.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/executors/executor.js +11 -0
- package/packages/gsd-cloud/dist/executors/executor.js.map +1 -0
- package/packages/gsd-cloud/dist/executors/gsd-pi-executor.d.ts +34 -0
- package/packages/gsd-cloud/dist/executors/gsd-pi-executor.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/executors/gsd-pi-executor.js +179 -0
- package/packages/gsd-cloud/dist/executors/gsd-pi-executor.js.map +1 -0
- package/packages/gsd-cloud/dist/executors/gsd-pi-executor.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/executors/gsd-pi-executor.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/executors/gsd-pi-executor.test.js +44 -0
- package/packages/gsd-cloud/dist/executors/gsd-pi-executor.test.js.map +1 -0
- package/packages/gsd-cloud/dist/executors/index.d.ts +17 -0
- package/packages/gsd-cloud/dist/executors/index.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/executors/index.js +29 -0
- package/packages/gsd-cloud/dist/executors/index.js.map +1 -0
- package/packages/gsd-cloud/dist/executors/mcp-stdio-client.d.ts +36 -0
- package/packages/gsd-cloud/dist/executors/mcp-stdio-client.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/executors/mcp-stdio-client.js +176 -0
- package/packages/gsd-cloud/dist/executors/mcp-stdio-client.js.map +1 -0
- package/packages/gsd-cloud/dist/executors/mcp-stdio-client.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/executors/mcp-stdio-client.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/executors/mcp-stdio-client.test.js +34 -0
- package/packages/gsd-cloud/dist/executors/mcp-stdio-client.test.js.map +1 -0
- package/packages/gsd-cloud/dist/index.d.ts +7 -0
- package/packages/gsd-cloud/dist/index.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/index.js +8 -0
- package/packages/gsd-cloud/dist/index.js.map +1 -0
- package/packages/gsd-cloud/dist/inject-gateway.d.ts +16 -0
- package/packages/gsd-cloud/dist/inject-gateway.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/inject-gateway.js +29 -0
- package/packages/gsd-cloud/dist/inject-gateway.js.map +1 -0
- package/packages/gsd-cloud/dist/inject-gateway.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/inject-gateway.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/inject-gateway.test.js +41 -0
- package/packages/gsd-cloud/dist/inject-gateway.test.js.map +1 -0
- package/packages/gsd-cloud/dist/logger.d.ts +25 -0
- package/packages/gsd-cloud/dist/logger.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/logger.js +72 -0
- package/packages/gsd-cloud/dist/logger.js.map +1 -0
- package/packages/gsd-cloud/dist/types.d.ts +51 -0
- package/packages/gsd-cloud/dist/types.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/types.js +9 -0
- package/packages/gsd-cloud/dist/types.js.map +1 -0
- package/packages/gsd-cloud/package.json +3 -3
- package/packages/mcp-server/README.md +8 -0
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +64 -2
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +4 -4
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +60 -0
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +54 -0
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/providers/amazon-bedrock.js +1 -0
- package/packages/pi-ai/dist/providers/amazon-bedrock.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +19 -0
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/package.json +2 -2
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- package/src/resources/GSD-WORKFLOW.md +16 -47
- package/src/resources/extensions/browser-tools/capture.ts +2 -86
- package/src/resources/extensions/browser-tools/screenshot-constraints.ts +94 -0
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +18 -1
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +108 -0
- package/src/resources/extensions/claude-code-cli/turn-assembler.ts +30 -2
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +2 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +86 -19
- package/src/resources/extensions/gsd/auto-post-unit.ts +4 -0
- package/src/resources/extensions/gsd/auto-prompts.ts +38 -1
- package/src/resources/extensions/gsd/auto.ts +5 -1
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +91 -4
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +80 -24
- package/src/resources/extensions/gsd/command-feedback.ts +19 -12
- package/src/resources/extensions/gsd/compat/compat-marker.ts +21 -4
- package/src/resources/extensions/gsd/db/command-queue.ts +7 -1
- package/src/resources/extensions/gsd/db/engine.ts +53 -17
- package/src/resources/extensions/gsd/db/writers/cascades.ts +5 -1
- package/src/resources/extensions/gsd/db/writers/status.ts +25 -8
- package/src/resources/extensions/gsd/db-base-schema.ts +30 -0
- package/src/resources/extensions/gsd/db-migration-steps.ts +32 -0
- package/src/resources/extensions/gsd/db-schema-metadata.ts +4 -1
- package/src/resources/extensions/gsd/docs/preferences-reference.md +4 -4
- package/src/resources/extensions/gsd/doctor-history.ts +81 -0
- package/src/resources/extensions/gsd/doctor-state-checks.ts +527 -0
- package/src/resources/extensions/gsd/doctor-workspace-checks.ts +97 -0
- package/src/resources/extensions/gsd/doctor.ts +14 -673
- package/src/resources/extensions/gsd/flat-phase-migration.ts +160 -19
- package/src/resources/extensions/gsd/gsd-db.ts +255 -52
- package/src/resources/extensions/gsd/guided-flow.ts +39 -10
- package/src/resources/extensions/gsd/markdown-renderer.ts +23 -7
- package/src/resources/extensions/gsd/md-importer.ts +116 -13
- package/src/resources/extensions/gsd/migrate/command.ts +21 -227
- package/src/resources/extensions/gsd/migrate/execution.ts +136 -0
- package/src/resources/extensions/gsd/migrate/plan.ts +101 -0
- package/src/resources/extensions/gsd/migrate/planning-writer.ts +21 -23
- package/src/resources/extensions/gsd/migrate/presentation.ts +83 -0
- package/src/resources/extensions/gsd/migration-auto-check.ts +3 -0
- package/src/resources/extensions/gsd/parallel-eligibility.ts +5 -4
- package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +37 -40
- package/src/resources/extensions/gsd/paths.ts +4 -1
- package/src/resources/extensions/gsd/phase-anchor.ts +8 -2
- package/src/resources/extensions/gsd/post-execution-checks.ts +4 -4
- package/src/resources/extensions/gsd/prompts/refine-slice.md +2 -2
- package/src/resources/extensions/gsd/safety/evidence-collector.ts +28 -1
- package/src/resources/extensions/gsd/safety/evidence-cross-ref.ts +31 -5
- package/src/resources/extensions/gsd/state-reconciliation/drift/external-markdown-edit.ts +30 -13
- package/src/resources/extensions/gsd/state-reconciliation/drift/external-planning-edit.ts +16 -3
- package/src/resources/extensions/gsd/state-reconciliation/drift/project-md.ts +67 -20
- package/src/resources/extensions/gsd/state-reconciliation/drift/sketch-flag.ts +47 -5
- package/src/resources/extensions/gsd/state-reconciliation/types.ts +2 -2
- package/src/resources/extensions/gsd/stop-notice.ts +18 -1
- package/src/resources/extensions/gsd/sync-lock.ts +14 -6
- package/src/resources/extensions/gsd/tests/closeout-git-deferral.test.ts +96 -1
- package/src/resources/extensions/gsd/tests/command-feedback.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/compat-marker.test.ts +104 -0
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +286 -0
- package/src/resources/extensions/gsd/tests/db-authority-regression.test.ts +76 -1
- package/src/resources/extensions/gsd/tests/db-engine-migrate-guards.test.ts +186 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/discuss-routing-fixes.test.ts +123 -0
- package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/dispatch-rule-coverage.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/doctor-history-public-api.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/evidence-cross-ref.test.ts +81 -0
- package/src/resources/extensions/gsd/tests/evidence-xref-gsd-exec.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/external-edit-scoped-import.test.ts +262 -0
- package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +202 -0
- package/src/resources/extensions/gsd/tests/gate-storage.test.ts +29 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/import-slice-milestone-completion-preserve.test.ts +116 -0
- package/src/resources/extensions/gsd/tests/migrate-hierarchy.test.ts +53 -0
- package/src/resources/extensions/gsd/tests/migrate-plan.test.ts +68 -0
- package/src/resources/extensions/gsd/tests/migrate-presentation.test.ts +98 -0
- package/src/resources/extensions/gsd/tests/migrate-safety-audit.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +53 -0
- package/src/resources/extensions/gsd/tests/milestone-leases.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/parallel-eligibility-file-overlap.test.ts +146 -0
- package/src/resources/extensions/gsd/tests/parallel-monitor-overlay.test.ts +59 -0
- package/src/resources/extensions/gsd/tests/parsers-legacy-importers.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/phase-anchor.test.ts +32 -4
- package/src/resources/extensions/gsd/tests/plan-task.test.ts +26 -23
- package/src/resources/extensions/gsd/tests/planning-writer.test.ts +47 -1
- package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +18 -3
- package/src/resources/extensions/gsd/tests/reassess-handler.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/replan-task-handler.test.ts +103 -0
- package/src/resources/extensions/gsd/tests/rework-brief.test.ts +197 -0
- package/src/resources/extensions/gsd/tests/skip-slice-cascades-tasks.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +177 -25
- package/src/resources/extensions/gsd/tests/stop-notice.test.ts +17 -0
- package/src/resources/extensions/gsd/tests/sync-lock.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +34 -0
- package/src/resources/extensions/gsd/tests/uok-plan-v2-wiring.test.ts +93 -1
- package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +2 -0
- package/src/resources/extensions/gsd/tests/workflow-migration-gates.test.ts +139 -0
- package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/write-gate-seam.test.ts +52 -0
- package/src/resources/extensions/gsd/tools/complete-task.ts +120 -0
- package/src/resources/extensions/gsd/tools/plan-task.ts +14 -16
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +18 -17
- package/src/resources/extensions/gsd/tools/replan-task.ts +160 -0
- package/src/resources/extensions/gsd/tools/rework-brief.ts +91 -0
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +92 -0
- package/src/resources/extensions/gsd/types.ts +11 -0
- package/src/resources/extensions/gsd/unit-registry.ts +4 -0
- package/src/resources/extensions/gsd/uok/plan-v2.ts +20 -3
- package/src/resources/extensions/gsd/worktree-state-projection.ts +12 -0
- package/src/resources/extensions/mac-tools/index.ts +52 -9
- package/src/resources/extensions/mac-tools/tests/mac-screenshot-payload.test.cjs +70 -0
- package/dist/web/standalone/.next/static/chunks/app/_not-found/page-49f565245e1e4afe.js +0 -1
- package/dist/web/standalone/.next/static/chunks/app/page-bc4c723c6a19c6f0.js +0 -1
- package/dist/web/standalone/.next/static/chunks/next/dist/client/components/builtin/global-error-ec3eaffc9785ba48.js +0 -1
- package/dist/web/standalone/.next/static/css/18c2fdf261bfc1b7.css +0 -1
- /package/dist/web/standalone/.next/static/{seHIUVbR1mm7LfG83nWJO → TezjdjSHBVRRr54XjAvte}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{seHIUVbR1mm7LfG83nWJO → TezjdjSHBVRRr54XjAvte}/_ssgManifest.js +0 -0
|
@@ -66,12 +66,12 @@ Then:
|
|
|
66
66
|
**Web apps:** when inlined Web App UAT guidance is present, follow it for Playwright scaffolding and browser-capable verification commands.
|
|
67
67
|
4. For non-trivial slices, plan observability / proof level / integration closure, threat surface, and requirement impact. Omit entirely for simple slices.
|
|
68
68
|
5. Decompose the slice into tasks that fit one context window each. Every task passed to `gsd_plan_slice` must use the exact keys `taskId`, `title`, `description`, `estimate`, `files`, `verify`, `inputs`, `expectedOutput`, and optional `observabilityImpact`. Put Why / Do / Done-when detail in `description`. `files`, `inputs`, and `expectedOutput` must be JSON arrays of strings, even for one path (for example, `"expectedOutput": ["src/index.ts"]`, never `"expectedOutput": "src/index.ts"`). `expectedOutput` is path-only: list only files the task creates or overwrites, and use `[]` for pure verification tasks. Never list GSD planning artifacts — anything under `.gsd/`, `.planning/`, or `.audits/`, or artifact names like `M001-CONTEXT.md` / `S01-PLAN.md` — in `inputs`, `files`, or `expectedOutput`: their content is preloaded as context, and they are written by workflow tools, not by tasks.
|
|
69
|
-
6. **Persist planning state through `gsd_plan_slice`.** Call it with the full payload. The tool writes to the DB and renders `{{outputPath}}`
|
|
69
|
+
6. **Persist planning state through `gsd_plan_slice`.** Call it with the full payload. The tool writes to the DB and renders `{{outputPath}}` with embedded task planning automatically. Do NOT rely on direct `PLAN.md` writes.
|
|
70
70
|
7. **Self-audit the plan.** If every task were completed exactly as written, the slice goal/demo should be true. Every must-have maps to a task. Inputs and Expected Output are backtick-wrapped file paths.
|
|
71
71
|
8. If refinement produced structural decisions that diverge from the sketch, call `gsd_decision_save` for each; the tool persists the decision and regenerates `.gsd/DECISIONS.md`.
|
|
72
72
|
9. {{commitInstruction}}
|
|
73
73
|
|
|
74
|
-
The slice
|
|
74
|
+
The slice plan path already exists. Do NOT mkdir.
|
|
75
75
|
|
|
76
76
|
**Autonomous execution:** Do not call `ask_user_questions` or `secure_env_collect`. Document assumptions in the plan.
|
|
77
77
|
|
|
@@ -232,7 +232,7 @@ export function recordToolCall(toolCallId: string, toolName: string, input: Reco
|
|
|
232
232
|
toolCallId,
|
|
233
233
|
// gsd_exec / gsd_uat_exec carry the script body in `script` (or `code`);
|
|
234
234
|
// bash-style tools use `command`/`cmd`; gsd_exec_search uses `query`.
|
|
235
|
-
command:
|
|
235
|
+
command: formatExecutionEvidenceCommand(toolName, input),
|
|
236
236
|
exitCode: -1,
|
|
237
237
|
outputSnippet: "",
|
|
238
238
|
timestamp: Date.now(),
|
|
@@ -254,6 +254,33 @@ export function recordToolCall(toolCallId: string, toolName: string, input: Reco
|
|
|
254
254
|
}
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
+
function pickString(input: Record<string, unknown>, ...keys: string[]): string {
|
|
258
|
+
for (const key of keys) {
|
|
259
|
+
const value = input[key];
|
|
260
|
+
if (typeof value === "string" && value.trim().length > 0) return value.trim();
|
|
261
|
+
}
|
|
262
|
+
return "";
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function canonicalExecutionToolLabel(toolName: string): string {
|
|
266
|
+
const normalized = toolName.trim().toLowerCase();
|
|
267
|
+
const mcpMatch = normalized.match(/^mcp__.+__(gsd_(?:uat_)?exec(?:_search)?)$/);
|
|
268
|
+
return mcpMatch?.[1] ?? normalized;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
function formatExecutionEvidenceCommand(toolName: string, input: Record<string, unknown>): string {
|
|
272
|
+
const body = pickString(input, "command", "script", "cmd", "code", "query");
|
|
273
|
+
const tool = canonicalExecutionToolLabel(toolName);
|
|
274
|
+
const purpose = pickString(input, "purpose");
|
|
275
|
+
const runtime = pickString(input, "runtime").toLowerCase();
|
|
276
|
+
const label = purpose && (tool === "gsd_exec" || tool === "gsd_uat_exec")
|
|
277
|
+
? `${tool}${runtime ? ` ${runtime}` : ""}: ${purpose}`
|
|
278
|
+
: "";
|
|
279
|
+
|
|
280
|
+
if (label && body) return `${label}\n${body}`;
|
|
281
|
+
return body || label;
|
|
282
|
+
}
|
|
283
|
+
|
|
257
284
|
/**
|
|
258
285
|
* Record a tool execution result. Matches the entry by toolCallId (assigned
|
|
259
286
|
* at dispatch time) and fills in exit code + output. Prior versions matched
|
|
@@ -161,9 +161,18 @@ function findMatches(
|
|
|
161
161
|
): BashEvidence[] {
|
|
162
162
|
const normalized = claimedCommand.trim();
|
|
163
163
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
164
|
+
const exact = bashCalls.filter((b) => b.command.trim() === normalized);
|
|
165
|
+
|
|
166
|
+
// When an exact run exists, also consider wrapper-equivalent reruns (e.g.
|
|
167
|
+
// `cd ... && <command>`) so a newer pass is not shadowed by a stale exact
|
|
168
|
+
// failure. Do not merge arbitrary containing scripts: their exit code may
|
|
169
|
+
// belong to later work, not to the claimed verification command.
|
|
170
|
+
const scriptWrapped = bashCalls.filter((b) => {
|
|
171
|
+
const command = b.command.trim();
|
|
172
|
+
if (command.length === 0 || command === normalized) return false;
|
|
173
|
+
return isWrapperEquivalentCommand(command, normalized);
|
|
174
|
+
});
|
|
175
|
+
if (exact.length > 0) return [...exact, ...scriptWrapped];
|
|
167
176
|
|
|
168
177
|
// Substring match: claimed is contained in actual or actual in claimed.
|
|
169
178
|
// A claimed verification command typically appears verbatim inside a
|
|
@@ -171,7 +180,7 @@ function findMatches(
|
|
|
171
180
|
// script-containing-claim is the common direction. Blank-command entries
|
|
172
181
|
// must be excluded — `"x".includes("")` is true, so they'd match anything.
|
|
173
182
|
const substring = bashCalls.filter(
|
|
174
|
-
b => b.command.trim().length > 0 &&
|
|
183
|
+
(b) => b.command.trim().length > 0 &&
|
|
175
184
|
(b.command.includes(normalized) || normalized.includes(b.command)),
|
|
176
185
|
);
|
|
177
186
|
if (substring.length > 0) return substring;
|
|
@@ -199,6 +208,23 @@ function findMatches(
|
|
|
199
208
|
|
|
200
209
|
function latestMatch(matches: readonly BashEvidence[]): BashEvidence {
|
|
201
210
|
return matches.reduce((latest, match) => (
|
|
202
|
-
match.timestamp
|
|
211
|
+
match.timestamp >= latest.timestamp ? match : latest
|
|
203
212
|
));
|
|
204
213
|
}
|
|
214
|
+
|
|
215
|
+
/** True when `actual` is the same command with only shell wrapper noise. */
|
|
216
|
+
function isWrapperEquivalentCommand(actual: string, claimed: string): boolean {
|
|
217
|
+
const claimIndex = actual.lastIndexOf(claimed);
|
|
218
|
+
if (claimIndex < 0) return false;
|
|
219
|
+
|
|
220
|
+
const prefix = actual.slice(0, claimIndex).trim();
|
|
221
|
+
if (prefix.length > 0 && !/^cd\s+.+\s+&&$/.test(prefix)) return false;
|
|
222
|
+
|
|
223
|
+
const suffix = actual.slice(claimIndex + claimed.length).trim();
|
|
224
|
+
return suffix.length === 0 || isBenignWrapperSuffix(suffix);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function isBenignWrapperSuffix(suffix: string): boolean {
|
|
228
|
+
if (/^;\s*echo\s+["']?[A-Z_]*EXIT=\$\?["']?$/.test(suffix)) return true;
|
|
229
|
+
return /^(?:(?:\d?>>?|&>)\s*\S+|\d?>&\d)(?:\s+(?:(?:\d?>>?|&>)\s*\S+|\d?>&\d))*$/.test(suffix);
|
|
230
|
+
}
|
|
@@ -3,9 +3,10 @@
|
|
|
3
3
|
//
|
|
4
4
|
// gsd-pi's DB is canonical, but .gsd/*.md is the inter-tool contract. When
|
|
5
5
|
// gsd-core edits a projection file, this handler detects the sha drift vs the
|
|
6
|
-
// recorded baseline in .gsd/.compat.json and re-imports
|
|
7
|
-
//
|
|
8
|
-
//
|
|
6
|
+
// recorded baseline in .gsd/.compat.json and re-imports from markdown with
|
|
7
|
+
// status authority scoped to the affected milestone ids. The next
|
|
8
|
+
// renderAllFromDb pass re-projects; the write-time invalidation hook then
|
|
9
|
+
// refreshes the marker entry, closing the loop.
|
|
9
10
|
|
|
10
11
|
import { existsSync, readFileSync } from "node:fs";
|
|
11
12
|
import { join } from "node:path";
|
|
@@ -59,10 +60,11 @@ function detectExternalMarkdownEdit(
|
|
|
59
60
|
}
|
|
60
61
|
|
|
61
62
|
/**
|
|
62
|
-
* Idempotent repair: re-import hierarchy
|
|
63
|
-
* markdown
|
|
64
|
-
*
|
|
65
|
-
*
|
|
63
|
+
* Idempotent repair: re-import the hierarchy from markdown while allowing
|
|
64
|
+
* markdown status authority only for milestones named by the drifted file's
|
|
65
|
+
* marker entities, then update the marker entry so the next detect pass sees
|
|
66
|
+
* the file as expected. migrateHierarchyToDb is itself idempotent (upsert), so
|
|
67
|
+
* re-running this repair after a successful one is a no-op.
|
|
66
68
|
*/
|
|
67
69
|
async function repairExternalMarkdownEdit(
|
|
68
70
|
record: ExternalMarkdownEditDrift,
|
|
@@ -72,13 +74,28 @@ async function repairExternalMarkdownEdit(
|
|
|
72
74
|
// Dynamic imports break a module-init cycle: this handler ← registry ←
|
|
73
75
|
// state.ts ← guided-flow.ts ← md-importer.ts ← (this handler's old static
|
|
74
76
|
// import). Deferring to repair time keeps module load cycle-free.
|
|
75
|
-
const { migrateHierarchyToDb } = await import("../../md-importer.js");
|
|
77
|
+
const { migrateHierarchyToDb, milestoneIdsFromEntities } = await import("../../md-importer.js");
|
|
76
78
|
const { invalidateStateCache } = await import("../../state.js");
|
|
77
|
-
//
|
|
78
|
-
//
|
|
79
|
-
//
|
|
80
|
-
//
|
|
81
|
-
|
|
79
|
+
// #027: migrateHierarchyToDb walks the whole tree (a cheap upsert), so
|
|
80
|
+
// without scoping every projection rides along with markdown *status*
|
|
81
|
+
// authority. That lets an unrelated file that is stale from gsd-pi's own
|
|
82
|
+
// miss silently revert a reopened slice/milestone. Scope status authority to
|
|
83
|
+
// exactly the milestone(s) this drifted file projects.
|
|
84
|
+
//
|
|
85
|
+
// Step 1: the marker's `entities` are DB ids (`M001`, `M001/S01`,
|
|
86
|
+
// `M001/S01/T01`), so the milestone id is always the first `/`-segment —
|
|
87
|
+
// layout-independent (legacy `milestones/<MID>/…` and flat `phases/NN-slug/…`
|
|
88
|
+
// both resolve the same way), no projectionPath parsing needed. An empty set
|
|
89
|
+
// (no resolvable entities) preserves DB status everywhere: fail toward
|
|
90
|
+
// protecting the DB, not toward markdown authority.
|
|
91
|
+
const statusAuthoritativeMilestones = milestoneIdsFromEntities(record.entities);
|
|
92
|
+
if (statusAuthoritativeMilestones.size === 0) {
|
|
93
|
+
logWarning(
|
|
94
|
+
"reconcile",
|
|
95
|
+
`external-markdown-edit: no milestone scope resolved for ${record.projectionPath}; preserving DB status for all milestones`,
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
migrateHierarchyToDb(ctx.basePath, { statusAuthoritativeMilestones });
|
|
82
99
|
invalidateStateCache();
|
|
83
100
|
} catch (err) {
|
|
84
101
|
logWarning(
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
// Parallel to external-markdown-edit.ts. Detects sha drift between the compat
|
|
4
4
|
// marker's planning.projections/passthrough and current .planning/ files.
|
|
5
5
|
//
|
|
6
|
-
// Modeled files (projections): re-imported via parsePlanningDirectory → DB
|
|
6
|
+
// Modeled files (projections): re-imported via parsePlanningDirectory → DB,
|
|
7
|
+
// with markdown status authority scoped to their marker entity milestone ids.
|
|
7
8
|
// Passthrough files (un-modeled docs): sha refreshed, content untouched.
|
|
8
9
|
|
|
9
10
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -148,13 +149,25 @@ async function repairExternalPlanningEdit(
|
|
|
148
149
|
const { parsePlanningDirectory } = await import("../../migrate/parser.js");
|
|
149
150
|
const { transformToGSD } = await import("../../migrate/transformer.js");
|
|
150
151
|
const { writeGSDDirectory } = await import("../../migrate/writer.js");
|
|
151
|
-
const { migrateHierarchyToDb } = await import("../../md-importer.js");
|
|
152
|
+
const { migrateHierarchyToDb, milestoneIdsFromEntities } = await import("../../md-importer.js");
|
|
152
153
|
const { invalidateStateCache } = await import("../../state.js");
|
|
153
154
|
|
|
154
155
|
const parsed = await parsePlanningDirectory(join(ctx.basePath, ".planning"));
|
|
155
156
|
const gsdProject = transformToGSD(parsed);
|
|
156
157
|
await writeGSDDirectory(gsdProject, ctx.basePath);
|
|
157
|
-
|
|
158
|
+
// #027: scope status authority to the milestone(s) this drifted .planning/
|
|
159
|
+
// file projects (first `/`-segment of its DB entity ids), so a stale
|
|
160
|
+
// checkbox in an unrelated projection can't revert a reopened slice/milestone
|
|
161
|
+
// in the DB. Unseeded files carry no entities → empty set → preserve DB
|
|
162
|
+
// status everywhere (fail toward the DB, log the breadcrumb).
|
|
163
|
+
const statusAuthoritativeMilestones = milestoneIdsFromEntities(record.entities);
|
|
164
|
+
if (statusAuthoritativeMilestones.size === 0) {
|
|
165
|
+
logWarning(
|
|
166
|
+
"reconcile",
|
|
167
|
+
`external-planning-edit: no milestone scope resolved for ${record.projectionPath}; preserving DB status for all milestones`,
|
|
168
|
+
);
|
|
169
|
+
}
|
|
170
|
+
migrateHierarchyToDb(ctx.basePath, { statusAuthoritativeMilestones });
|
|
158
171
|
invalidateStateCache();
|
|
159
172
|
} catch (err) {
|
|
160
173
|
logWarning(
|
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
|
|
8
8
|
import { existsSync } from "node:fs";
|
|
9
9
|
|
|
10
|
-
import { getMilestone, isDbAvailable } from "../../gsd-db.js";
|
|
11
|
-
import { findMilestoneIds } from "../../milestone-ids.js";
|
|
10
|
+
import { getAllMilestones, getMilestone, isDbAvailable } from "../../gsd-db.js";
|
|
11
|
+
import { findMilestoneIds, parseMilestoneId } from "../../milestone-ids.js";
|
|
12
12
|
import { resolveMilestoneFile, resolveMilestonePath } from "../../paths.js";
|
|
13
13
|
import type { GSDState } from "../../types.js";
|
|
14
14
|
import type { DriftContext, DriftHandler, DriftRecord } from "../types.js";
|
|
@@ -29,6 +29,26 @@ function milestoneHasContent(basePath: string, milestoneId: string): boolean {
|
|
|
29
29
|
);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/**
|
|
33
|
+
* A directory extracted to a bare `M{NNN}` id is not unregistered when the
|
|
34
|
+
* milestone is actually registered under a `unique_milestone_ids` suffixed id
|
|
35
|
+
* (`M{NNN}-<suffix>`) for the same sequence number. The flat-phase extractor in
|
|
36
|
+
* `milestone-ids` cannot recover the suffix from a descriptive slug (e.g.
|
|
37
|
+
* `07-v40fmq-m007-...-footer-system`), so it falls through to the bare `M007`.
|
|
38
|
+
* Probing the DB for a suffixed row on that sequence resolves the directory to
|
|
39
|
+
* its registered identity instead of firing a false-positive drift (issue
|
|
40
|
+
* #1281).
|
|
41
|
+
*/
|
|
42
|
+
function hasRegisteredSuffixedVariant(milestoneId: string): boolean {
|
|
43
|
+
const { suffix, num } = parseMilestoneId(milestoneId);
|
|
44
|
+
// Only bare, well-formed `M{NNN}` ids can be ambiguous with a suffixed row.
|
|
45
|
+
if (suffix || num === 0) return false;
|
|
46
|
+
return getAllMilestones().some((m) => {
|
|
47
|
+
const parsed = parseMilestoneId(m.id);
|
|
48
|
+
return parsed.num === num && parsed.suffix !== undefined;
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
|
|
32
52
|
export function detectUnregisteredMilestoneDrift(
|
|
33
53
|
_state: GSDState,
|
|
34
54
|
ctx: DriftContext,
|
|
@@ -38,6 +58,7 @@ export function detectUnregisteredMilestoneDrift(
|
|
|
38
58
|
const drifts: UnregisteredMilestoneDrift[] = [];
|
|
39
59
|
for (const milestoneId of findMilestoneIds(ctx.basePath)) {
|
|
40
60
|
if (getMilestone(milestoneId)) continue;
|
|
61
|
+
if (hasRegisteredSuffixedVariant(milestoneId)) continue;
|
|
41
62
|
if (!milestoneHasContent(ctx.basePath, milestoneId)) continue;
|
|
42
63
|
drifts.push({ kind: "unregistered-milestone", milestoneId });
|
|
43
64
|
}
|
|
@@ -45,35 +66,61 @@ export function detectUnregisteredMilestoneDrift(
|
|
|
45
66
|
}
|
|
46
67
|
|
|
47
68
|
/**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
54
|
-
*
|
|
55
|
-
* directory — not a full DB reimport. `/gsd recover --confirm` is a destructive
|
|
56
|
-
* clear-and-reimport of the entire DB and is offered only as a last resort, so
|
|
57
|
-
* users do not reach for it expecting a targeted repair (see issue #826).
|
|
69
|
+
* The recovery hint deliberately leads with the *targeted*, non-destructive
|
|
70
|
+
* options. The common cause of this drift is a directory left under an old ID
|
|
71
|
+
* after a `unique_milestone_ids` rename, where the right fix is to rename (move)
|
|
72
|
+
* the directory — not a full DB reimport. `/gsd recover --confirm` is a
|
|
73
|
+
* destructive clear-and-reimport of the entire DB and is offered only as a last
|
|
74
|
+
* resort, so users do not reach for it expecting a targeted repair (see issue
|
|
75
|
+
* #826).
|
|
58
76
|
*/
|
|
59
|
-
|
|
77
|
+
function unregisteredMilestoneGuidance(
|
|
60
78
|
record: UnregisteredMilestoneDrift,
|
|
61
79
|
ctx: DriftContext,
|
|
62
|
-
):
|
|
80
|
+
): string {
|
|
63
81
|
const dir = resolveMilestonePath(ctx.basePath, record.milestoneId);
|
|
64
82
|
const dirHint = dir ?? `the .gsd directory for ${record.milestoneId}`;
|
|
65
|
-
|
|
83
|
+
return (
|
|
66
84
|
`Milestone ${record.milestoneId} exists only as markdown projection ` +
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
85
|
+
"(on-disk ROADMAP/CONTEXT/SUMMARY with no authoritative DB row). " +
|
|
86
|
+
"Runtime reconciliation will not import markdown into the DB. Choose one:\n" +
|
|
87
|
+
` • Rename: if this directory is the same milestone under an old ID (e.g. a unique_milestone_ids rename), move \`${dirHint}\` to the current ID's directory and re-run.\n` +
|
|
88
|
+
` • Discard: if this milestone is no longer relevant, delete \`${dirHint}\` and re-run.\n` +
|
|
89
|
+
" • Last resort: `/gsd recover --confirm` performs a destructive full DB clear-and-reimport — it does NOT do a targeted import and can replace or duplicate existing DB milestones."
|
|
72
90
|
);
|
|
73
91
|
}
|
|
74
92
|
|
|
93
|
+
/**
|
|
94
|
+
* Terminal blocker for an unregistered milestone. The project-root DB is
|
|
95
|
+
* authoritative at runtime; markdown-only milestones must be reconciled through
|
|
96
|
+
* an explicit, operator-controlled action so operators opt into changing
|
|
97
|
+
* canonical state. Exposing this as a `blocker` (matching the `artifact-db`
|
|
98
|
+
* convention) surfaces the drift as a pause-with-hint that gates dispatch,
|
|
99
|
+
* instead of a guaranteed repair throw that escalates auto-mode health to red
|
|
100
|
+
* (see issue #1281).
|
|
101
|
+
*/
|
|
102
|
+
export function describeUnregisteredMilestoneBlocker(
|
|
103
|
+
record: UnregisteredMilestoneDrift,
|
|
104
|
+
ctx: DriftContext,
|
|
105
|
+
): string {
|
|
106
|
+
return unregisteredMilestoneGuidance(record, ctx);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Repair intentionally fails closed. Retained as a defensive fallback for the
|
|
111
|
+
* reconciliation engine; in practice `blocker` short-circuits this drift into a
|
|
112
|
+
* non-fatal pause before repair is ever attempted.
|
|
113
|
+
*/
|
|
114
|
+
export function repairUnregisteredMilestone(
|
|
115
|
+
record: UnregisteredMilestoneDrift,
|
|
116
|
+
ctx: DriftContext,
|
|
117
|
+
): void {
|
|
118
|
+
throw new Error(unregisteredMilestoneGuidance(record, ctx));
|
|
119
|
+
}
|
|
120
|
+
|
|
75
121
|
export const unregisteredMilestoneHandler: DriftHandler<UnregisteredMilestoneDrift> = {
|
|
76
122
|
kind: "unregistered-milestone",
|
|
77
123
|
detect: detectUnregisteredMilestoneDrift,
|
|
124
|
+
blocker: describeUnregisteredMilestoneBlocker,
|
|
78
125
|
repair: repairUnregisteredMilestone,
|
|
79
126
|
};
|
|
@@ -8,13 +8,14 @@
|
|
|
8
8
|
// sketch slices to plan-slice, which writes PLAN.md but leaves
|
|
9
9
|
// is_sketch=1 — the next reconciliation pass clears it.
|
|
10
10
|
|
|
11
|
-
import { existsSync } from "node:fs";
|
|
11
|
+
import { existsSync, readFileSync } from "node:fs";
|
|
12
12
|
|
|
13
13
|
import {
|
|
14
14
|
getSketchedSliceIds,
|
|
15
15
|
isDbAvailable,
|
|
16
16
|
setSliceSketchFlag,
|
|
17
17
|
} from "../../gsd-db.js";
|
|
18
|
+
import { parsePlan } from "../../parsers-legacy.js";
|
|
18
19
|
import { resolveSliceFile } from "../../paths.js";
|
|
19
20
|
import type { GSDState } from "../../types.js";
|
|
20
21
|
import type { DriftContext, DriftHandler, DriftRecord } from "../types.js";
|
|
@@ -31,13 +32,54 @@ export function detectStaleSketchFlags(
|
|
|
31
32
|
|
|
32
33
|
const sliceIds = getSketchedSliceIds(mid);
|
|
33
34
|
return sliceIds
|
|
34
|
-
.filter((sid) =>
|
|
35
|
-
const planPath = resolveSliceFile(ctx.basePath, mid, sid, "PLAN");
|
|
36
|
-
return planPath !== null && existsSync(planPath);
|
|
37
|
-
})
|
|
35
|
+
.filter((sid) => sketchIsPlanned(ctx.basePath, mid, sid))
|
|
38
36
|
.map((sid) => ({ kind: "stale-sketch-flag" as const, mid, sid }));
|
|
39
37
|
}
|
|
40
38
|
|
|
39
|
+
/**
|
|
40
|
+
* True when a task title is a synthetic placeholder emitted by a projection
|
|
41
|
+
* round-trip rather than a real, refined task. `migrate/transformer.buildTaskTitle`
|
|
42
|
+
* produces two shapes when a plan was never decomposed:
|
|
43
|
+
* - `Plan NN` (fallback when phase/plan frontmatter is absent)
|
|
44
|
+
* - `${phase} ${plan}` (e.g. `00 01`, `00 03b`, `29-auth-system 01`)
|
|
45
|
+
* Neither carries genuine planning intent, so both must be treated as stubs.
|
|
46
|
+
*/
|
|
47
|
+
function isPlaceholderTaskTitle(title: string): boolean {
|
|
48
|
+
const t = title.trim();
|
|
49
|
+
if (/^Plan\s+\d+[a-z]*$/i.test(t)) return true;
|
|
50
|
+
// buildTaskTitle returns `${phase} ${plan}` when frontmatter has both fields.
|
|
51
|
+
// Phase slugs are digit-led (e.g. `00`, `29-auth-system`); require that so
|
|
52
|
+
// legitimate titles like `Step 1` are not misclassified as stubs.
|
|
53
|
+
return /^\d[\w-]*\s+\d+[a-z]*$/i.test(t);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* A sketch slice counts as "planned" (so its is_sketch flag may be cleared)
|
|
58
|
+
* only when it has a *real* plan on disk — not merely any PLAN file.
|
|
59
|
+
*
|
|
60
|
+
* File existence alone (#1287) is too weak: a stub/placeholder PLAN, a
|
|
61
|
+
* crash-leftover, or a projection round-trip that emits synthetic placeholder
|
|
62
|
+
* task titles (migrate/transformer.buildTaskTitle) all satisfy existsSync yet
|
|
63
|
+
* were never actually refined. Clearing the flag for one strips the `refining`
|
|
64
|
+
* guard in phase derivation and lets a phantom task drive dispatch.
|
|
65
|
+
*
|
|
66
|
+
* A legitimate plan-slice always decomposes into >= 1 genuine task, so require
|
|
67
|
+
* at least one non-placeholder task. This is strictly stricter than the old
|
|
68
|
+
* existence check — the documented crash-recovery scenarios (a real plan-slice
|
|
69
|
+
* that wrote its tasks) still clear, a bare stub no longer does.
|
|
70
|
+
*/
|
|
71
|
+
function sketchIsPlanned(basePath: string, mid: string, sid: string): boolean {
|
|
72
|
+
const planPath = resolveSliceFile(basePath, mid, sid, "PLAN");
|
|
73
|
+
if (planPath === null || !existsSync(planPath)) return false;
|
|
74
|
+
try {
|
|
75
|
+
const plan = parsePlan(readFileSync(planPath, "utf-8"));
|
|
76
|
+
return plan.tasks.some((t) => !isPlaceholderTaskTitle(t.title));
|
|
77
|
+
} catch {
|
|
78
|
+
// A PLAN we cannot parse is not a trustworthy "planning done" signal.
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
41
83
|
export function repairStaleSketchFlag(record: SketchFlagDrift): void {
|
|
42
84
|
setSliceSketchFlag(record.mid, record.sid, false);
|
|
43
85
|
}
|
|
@@ -49,14 +49,14 @@ export type DriftRecord =
|
|
|
49
49
|
projectionPath: string; // relative to basePath, e.g. "m1/plan.md"
|
|
50
50
|
expectedSha: string; // sha recorded in .compat.json
|
|
51
51
|
actualSha: string; // freshly computed from disk
|
|
52
|
-
entities: string[]; // DB entity ids to
|
|
52
|
+
entities: string[]; // DB entity ids used to scope status authority
|
|
53
53
|
}
|
|
54
54
|
| {
|
|
55
55
|
kind: "external-planning-edit";
|
|
56
56
|
projectionPath: string; // relative to .planning/, e.g. "phases/01-foo/01-01-PLAN.md"
|
|
57
57
|
expectedSha: string;
|
|
58
58
|
actualSha: string;
|
|
59
|
-
entities: string[];
|
|
59
|
+
entities: string[]; // DB entity ids used to scope status authority
|
|
60
60
|
passthrough: boolean; // true = content-only, no re-import, just refresh sha
|
|
61
61
|
};
|
|
62
62
|
|
|
@@ -66,10 +66,27 @@ export function isNonBlockingPauseNotice(message: string): boolean {
|
|
|
66
66
|
return message.includes("idempotent advance: unit already active");
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
+
/**
|
|
70
|
+
* A picker / next-action menu that could not render in a non-interactive
|
|
71
|
+
* (headless / RPC) session. Both wordings originate from the menu-unavailable
|
|
72
|
+
* helpers (`notifyCommandMenuUnavailable` and `notifyPickerCommandNeedsInteractiveMenu`
|
|
73
|
+
* in next-action-ui.ts / command-feedback.ts). Headless `auto`/`next` cannot
|
|
74
|
+
* answer such a menu, so the run has dead-ended and needs operator intervention.
|
|
75
|
+
* Classify it as blocked so the headless host exits 10 instead of idling
|
|
76
|
+
* forever waiting for a completion signal that never comes. (#1294)
|
|
77
|
+
*/
|
|
78
|
+
export function isInteractiveMenuUnavailableNotice(message: string): boolean {
|
|
79
|
+
return (
|
|
80
|
+
message.includes("menu could not be shown in this session") ||
|
|
81
|
+
message.includes("did not start:")
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
69
85
|
export function isBlockedNoticeMessage(message: string): boolean {
|
|
70
86
|
return (
|
|
71
87
|
message.includes("blocked:") ||
|
|
72
88
|
(isPauseNotice(message) && !isNonBlockingPauseNotice(message)) ||
|
|
73
|
-
isManualResolutionNotice(message)
|
|
89
|
+
isManualResolutionNotice(message) ||
|
|
90
|
+
isInteractiveMenuUnavailableNotice(message)
|
|
74
91
|
);
|
|
75
92
|
}
|
|
@@ -9,8 +9,10 @@ import { dirname, join } from "node:path";
|
|
|
9
9
|
const STALE_THRESHOLD_MS = 60_000; // 60 seconds
|
|
10
10
|
const DEFAULT_TIMEOUT_MS = 0; // fail fast; sync waits block the JS event loop
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
const DEFAULT_LOCK_NAME = "sync.lock";
|
|
13
|
+
|
|
14
|
+
function lockFilePath(basePath: string, lockName: string = DEFAULT_LOCK_NAME): string {
|
|
15
|
+
return join(basePath, ".gsd", lockName);
|
|
14
16
|
}
|
|
15
17
|
|
|
16
18
|
/** True if the given PID is alive in the current process namespace. */
|
|
@@ -74,8 +76,9 @@ function tryCreateLockFile(lp: string, payload: string): boolean {
|
|
|
74
76
|
export function acquireSyncLock(
|
|
75
77
|
basePath: string,
|
|
76
78
|
_timeoutMs: number = DEFAULT_TIMEOUT_MS,
|
|
79
|
+
lockName: string = DEFAULT_LOCK_NAME,
|
|
77
80
|
): { acquired: boolean } {
|
|
78
|
-
const lp = lockFilePath(basePath);
|
|
81
|
+
const lp = lockFilePath(basePath, lockName);
|
|
79
82
|
const lockData = JSON.stringify(
|
|
80
83
|
{ pid: process.pid, acquired_at: new Date().toISOString() },
|
|
81
84
|
null,
|
|
@@ -89,7 +92,12 @@ export function acquireSyncLock(
|
|
|
89
92
|
return { acquired: true };
|
|
90
93
|
}
|
|
91
94
|
} catch {
|
|
92
|
-
|
|
95
|
+
// tryCreateLockFile only throws for non-EEXIST errors — a persistently
|
|
96
|
+
// uncreatable lock path (dangling-symlink parent, ENOTDIR, read-only FS).
|
|
97
|
+
// Retrying would spin the loop forever with no progress condition,
|
|
98
|
+
// blocking the JS event loop. Fail open like a contended lock so callers
|
|
99
|
+
// proceed (racy but functional) instead of hanging.
|
|
100
|
+
return { acquired: false };
|
|
93
101
|
}
|
|
94
102
|
|
|
95
103
|
// File exists. Decide whether to steal (stale + owner dead) or skip.
|
|
@@ -131,8 +139,8 @@ export function acquireSyncLock(
|
|
|
131
139
|
/**
|
|
132
140
|
* Release the advisory sync lock. No-op if lock file does not exist.
|
|
133
141
|
*/
|
|
134
|
-
export function releaseSyncLock(basePath: string): void {
|
|
135
|
-
const lp = lockFilePath(basePath);
|
|
142
|
+
export function releaseSyncLock(basePath: string, lockName: string = DEFAULT_LOCK_NAME): void {
|
|
143
|
+
const lp = lockFilePath(basePath, lockName);
|
|
136
144
|
try {
|
|
137
145
|
if (existsSync(lp)) {
|
|
138
146
|
unlinkSync(lp);
|
|
@@ -3,8 +3,14 @@
|
|
|
3
3
|
|
|
4
4
|
import test from "node:test";
|
|
5
5
|
import assert from "node:assert/strict";
|
|
6
|
+
import { writeFileSync } from "node:fs";
|
|
7
|
+
import { join } from "node:path";
|
|
6
8
|
|
|
7
|
-
import { shouldDeferCloseoutGitAction } from "../auto-post-unit.ts";
|
|
9
|
+
import { postUnitPreVerification, shouldDeferCloseoutGitAction, type PostUnitContext } from "../auto-post-unit.ts";
|
|
10
|
+
import { AutoSession } from "../auto/session.ts";
|
|
11
|
+
import { closeDatabase, insertMilestone, insertSlice, insertTask, insertVerificationEvidence, openDatabase } from "../gsd-db.ts";
|
|
12
|
+
import { recordToolCall, recordToolResult, resetEvidence } from "../safety/evidence-collector.ts";
|
|
13
|
+
import { cleanup, git, makeTempRepo } from "./test-utils.ts";
|
|
8
14
|
|
|
9
15
|
test("execute-task defers closeout git action until verification passes", () => {
|
|
10
16
|
assert.equal(shouldDeferCloseoutGitAction("execute-task"), true);
|
|
@@ -14,3 +20,92 @@ test("non execute-task units keep pre-verification closeout git action", () => {
|
|
|
14
20
|
assert.equal(shouldDeferCloseoutGitAction("plan-slice"), false);
|
|
15
21
|
assert.equal(shouldDeferCloseoutGitAction("complete-slice"), false);
|
|
16
22
|
});
|
|
23
|
+
|
|
24
|
+
test("blocking evidence-xref commits deferred execute-task work before pausing", async () => {
|
|
25
|
+
const base = makeTempRepo("gsd-evidence-xref-commit-before-pause-");
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
writeFileSync(join(base, ".gitignore"), ".gsd/\n");
|
|
29
|
+
git(base, "add", ".gitignore");
|
|
30
|
+
git(base, "commit", "-m", "chore: ignore gsd runtime");
|
|
31
|
+
|
|
32
|
+
openDatabase(":memory:");
|
|
33
|
+
insertMilestone({ id: "M001", title: "Milestone", status: "active" });
|
|
34
|
+
insertSlice({ id: "S01", milestoneId: "M001", title: "Slice", status: "active" });
|
|
35
|
+
insertTask({
|
|
36
|
+
id: "T01",
|
|
37
|
+
sliceId: "S01",
|
|
38
|
+
milestoneId: "M001",
|
|
39
|
+
title: "Add app entrypoint",
|
|
40
|
+
status: "complete",
|
|
41
|
+
oneLiner: "Added app entrypoint",
|
|
42
|
+
keyFiles: ["app.js"],
|
|
43
|
+
planning: {
|
|
44
|
+
description: "Create app entrypoint",
|
|
45
|
+
estimate: "small",
|
|
46
|
+
files: ["app.js"],
|
|
47
|
+
verify: "npm test",
|
|
48
|
+
inputs: [],
|
|
49
|
+
expectedOutput: ["app.js"],
|
|
50
|
+
observabilityImpact: "none",
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
insertVerificationEvidence({
|
|
54
|
+
taskId: "T01",
|
|
55
|
+
sliceId: "S01",
|
|
56
|
+
milestoneId: "M001",
|
|
57
|
+
command: "npm test",
|
|
58
|
+
exitCode: 0,
|
|
59
|
+
verdict: "passed",
|
|
60
|
+
durationMs: 10,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
writeFileSync(join(base, "app.js"), "console.log('ready');\n");
|
|
64
|
+
resetEvidence();
|
|
65
|
+
recordToolCall("call-1", "bash", { command: "npm test" });
|
|
66
|
+
recordToolResult("call-1", "bash", "Command exited with code 1\nfailed\n", true);
|
|
67
|
+
|
|
68
|
+
const s = new AutoSession();
|
|
69
|
+
s.active = true;
|
|
70
|
+
s.basePath = base;
|
|
71
|
+
s.currentUnit = { type: "execute-task", id: "M001/S01/T01", startedAt: Date.now() };
|
|
72
|
+
|
|
73
|
+
let pauseCalled = false;
|
|
74
|
+
const notifications: string[] = [];
|
|
75
|
+
const pctx: PostUnitContext = {
|
|
76
|
+
s,
|
|
77
|
+
ctx: {
|
|
78
|
+
ui: { notify: (message: string) => notifications.push(message) },
|
|
79
|
+
} as unknown as PostUnitContext["ctx"],
|
|
80
|
+
pi: {} as PostUnitContext["pi"],
|
|
81
|
+
buildSnapshotOpts: () => ({}),
|
|
82
|
+
lockBase: () => base,
|
|
83
|
+
stopAuto: async () => {},
|
|
84
|
+
pauseAuto: async () => {
|
|
85
|
+
pauseCalled = true;
|
|
86
|
+
assert.equal(git(base, "status", "--short"), "", "task work must be committed before pauseAuto runs");
|
|
87
|
+
},
|
|
88
|
+
updateProgressWidget: () => {},
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const result = await postUnitPreVerification(pctx, {
|
|
92
|
+
skipSettleDelay: true,
|
|
93
|
+
skipWorktreeSync: true,
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
assert.equal(result, "dispatched");
|
|
97
|
+
assert.equal(pauseCalled, true);
|
|
98
|
+
assert.ok(
|
|
99
|
+
notifications.some((message) => message.includes("claimed passing verification")),
|
|
100
|
+
`expected evidence-xref notification, got: ${notifications.join("\n")}`,
|
|
101
|
+
);
|
|
102
|
+
|
|
103
|
+
const commitMessage = git(base, "log", "-1", "--pretty=%B");
|
|
104
|
+
assert.match(commitMessage, /^feat: Added app entrypoint/m);
|
|
105
|
+
assert.match(commitMessage, /GSD-Task: S01\/T01/);
|
|
106
|
+
} finally {
|
|
107
|
+
resetEvidence();
|
|
108
|
+
closeDatabase();
|
|
109
|
+
cleanup(base);
|
|
110
|
+
}
|
|
111
|
+
});
|