@opengsd/gsd-pi 1.8.1 → 1.9.0
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 +10 -8
- 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/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 +102 -14
- 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 +12 -17
- package/dist/resources/extensions/gsd/migrate/presentation.js +64 -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/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 +8 -8
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +2 -2
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- 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 +2 -2
- 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/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +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 +8 -8
- package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-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/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-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/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 +109 -11
- 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 +12 -20
- package/src/resources/extensions/gsd/migrate/presentation.ts +83 -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/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-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/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-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/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/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 +29 -0
- package/src/resources/extensions/gsd/tests/post-execution-checks.test.ts +22 -0
- 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/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/css/18c2fdf261bfc1b7.css +0 -1
- /package/dist/web/standalone/.next/static/{UaXRrOYxBpG9QBINKhZkg → BE5XoklToI6lZjRzpXih8}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{UaXRrOYxBpG9QBINKhZkg → BE5XoklToI6lZjRzpXih8}/_ssgManifest.js +0 -0
|
@@ -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(
|
|
@@ -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
|
+
});
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
notifySmartEntryNeedsInteractiveMenu,
|
|
12
12
|
requiresInteractiveMenu,
|
|
13
13
|
} from "../command-feedback.js";
|
|
14
|
+
import { isBlockedNoticeMessage, isInteractiveMenuUnavailableNotice } from "../stop-notice.js";
|
|
14
15
|
import type { ExtensionCommandContext } from "@gsd/pi-coding-agent";
|
|
15
16
|
|
|
16
17
|
function makeCtx(overrides: {
|
|
@@ -87,6 +88,17 @@ describe("notifyQueueHubNeedsInteractiveMenu", () => {
|
|
|
87
88
|
assert.match(notes[0]!.message, /\/gsd queue/);
|
|
88
89
|
assert.match(notes[0]!.message, /reorder/i);
|
|
89
90
|
});
|
|
91
|
+
|
|
92
|
+
// /gsd queue continues headless with the add-work flow after this notice, so
|
|
93
|
+
// it must NOT be classified as a blocked dead-end (would exit 10 early). (#1294)
|
|
94
|
+
it("emits a notice the headless host does not treat as a dead-end", () => {
|
|
95
|
+
const ctx = makeCtx({ hasUI: false });
|
|
96
|
+
notifyQueueHubNeedsInteractiveMenu(ctx, "this session has no interactive menu");
|
|
97
|
+
const notes = (ctx as typeof ctx & { _notifications: Array<{ message: string }> })._notifications;
|
|
98
|
+
const message = notes[0]!.message.toLowerCase();
|
|
99
|
+
assert.equal(isInteractiveMenuUnavailableNotice(message), false);
|
|
100
|
+
assert.equal(isBlockedNoticeMessage(message), false);
|
|
101
|
+
});
|
|
90
102
|
});
|
|
91
103
|
|
|
92
104
|
describe("notifyInitNeedsInteractiveMenu", () => {
|
|
@@ -16,6 +16,9 @@ import {
|
|
|
16
16
|
EMPTY_MARKER,
|
|
17
17
|
compatMarkerPath,
|
|
18
18
|
} from "../compat/compat-marker.ts";
|
|
19
|
+
import { externalMarkdownEditHandler } from "../state-reconciliation/drift/external-markdown-edit.ts";
|
|
20
|
+
import type { DriftContext } from "../state-reconciliation/types.ts";
|
|
21
|
+
import type { GSDState } from "../types.ts";
|
|
19
22
|
|
|
20
23
|
const tmpDirs: string[] = [];
|
|
21
24
|
|
|
@@ -188,3 +191,104 @@ test("pruneOrphanedProjectionEntries returns 0 and writes nothing when marker is
|
|
|
188
191
|
const files = readdirSync(join(base, ".gsd"));
|
|
189
192
|
assert.ok(!files.includes(".compat.json"), "must not create a marker where none existed");
|
|
190
193
|
});
|
|
194
|
+
|
|
195
|
+
// --- Path-traversal containment (plan 029) ---------------------------------
|
|
196
|
+
//
|
|
197
|
+
// The marker is repo-controlled content whose projection-map keys are joined
|
|
198
|
+
// with basePath and readFileSync'd by the drift detectors. A key that escapes
|
|
199
|
+
// .gsd/ must invalidate the whole marker so readCompatMarker fails safe to the
|
|
200
|
+
// empty marker (detectors see no entries → nothing outside the repo is read).
|
|
201
|
+
|
|
202
|
+
for (const badKey of ["../outside.md", "/etc/hosts", "C:/x.md", "..\\x.md"]) {
|
|
203
|
+
test(`readCompatMarker rejects a projection key that escapes the root: ${JSON.stringify(badKey)}`, () => {
|
|
204
|
+
const base = makeTmpBase();
|
|
205
|
+
// Write the hostile marker directly (writeCompatMarker does not validate).
|
|
206
|
+
writeCompatMarker(base, {
|
|
207
|
+
schema: 2,
|
|
208
|
+
lastWriter: "gsd-pi",
|
|
209
|
+
lastProjectedAt: "2026-07-07T00:00:00.000Z",
|
|
210
|
+
projections: { [badKey]: { sha: "deadbeefdeadbeef", entities: ["m1"] } },
|
|
211
|
+
piVersion: "1.8.1",
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
const marker = readCompatMarker(base);
|
|
215
|
+
assert.equal(
|
|
216
|
+
Object.keys(marker.projections).length,
|
|
217
|
+
0,
|
|
218
|
+
"a marker with an escaping key must fall back to the empty marker",
|
|
219
|
+
);
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
test("readCompatMarker preserves legitimate nested projection keys", () => {
|
|
224
|
+
const base = makeTmpBase();
|
|
225
|
+
const roadmap = "milestones/M001/M001-ROADMAP.md";
|
|
226
|
+
const context = "phases/01-foo/01-CONTEXT.md";
|
|
227
|
+
writeCompatMarker(base, {
|
|
228
|
+
schema: 2,
|
|
229
|
+
lastWriter: "gsd-pi",
|
|
230
|
+
lastProjectedAt: "2026-07-07T00:00:00.000Z",
|
|
231
|
+
projections: {
|
|
232
|
+
[roadmap]: { sha: "aaaaaaaaaaaaaaaa", entities: ["M001"] },
|
|
233
|
+
[context]: { sha: "bbbbbbbbbbbbbbbb", entities: ["P01"] },
|
|
234
|
+
},
|
|
235
|
+
planning: { active: false, layout: null, projections: {}, passthrough: {} },
|
|
236
|
+
piVersion: "1.8.1",
|
|
237
|
+
});
|
|
238
|
+
|
|
239
|
+
const marker = readCompatMarker(base);
|
|
240
|
+
assert.ok(marker.projections[roadmap], "roadmap key must round-trip");
|
|
241
|
+
assert.ok(marker.projections[context], "nested phase key must round-trip");
|
|
242
|
+
assert.equal(Object.keys(marker.projections).length, 2);
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
test("an escaping key in planning.passthrough also invalidates the whole marker", () => {
|
|
246
|
+
const base = makeTmpBase();
|
|
247
|
+
writeCompatMarker(base, {
|
|
248
|
+
schema: 2,
|
|
249
|
+
lastWriter: "gsd-pi",
|
|
250
|
+
lastProjectedAt: "2026-07-07T00:00:00.000Z",
|
|
251
|
+
projections: {},
|
|
252
|
+
planning: {
|
|
253
|
+
active: true,
|
|
254
|
+
layout: "flat-phases",
|
|
255
|
+
projections: {},
|
|
256
|
+
passthrough: { "../../secret.md": { sha: "cccccccccccccccc", entities: [] } },
|
|
257
|
+
},
|
|
258
|
+
piVersion: "1.8.1",
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
const marker = readCompatMarker(base);
|
|
262
|
+
assert.equal(Object.keys(marker.projections).length, 0);
|
|
263
|
+
assert.equal(marker.planning!.active, false, "planning falls back to inactive default");
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
test("hostile marker makes the drift detector read nothing outside the project", async () => {
|
|
267
|
+
const base = makeTmpBase();
|
|
268
|
+
// Sentinel lives OUTSIDE the project dir; a `../` traversal from .gsd/ would
|
|
269
|
+
// reach it if the key were trusted.
|
|
270
|
+
const sentinelDir = mkdtempSync(join(tmpdir(), `gsd-sentinel-${randomUUID()}`));
|
|
271
|
+
tmpDirs.push(sentinelDir);
|
|
272
|
+
const sentinelName = "known_hosts";
|
|
273
|
+
writeFileSync(join(sentinelDir, sentinelName), "SECRET\n", "utf-8");
|
|
274
|
+
const escapeKey = `../../${sentinelName}`; // e.g. ../../known_hosts
|
|
275
|
+
|
|
276
|
+
writeCompatMarker(base, {
|
|
277
|
+
schema: 2,
|
|
278
|
+
lastWriter: "gsd-pi",
|
|
279
|
+
lastProjectedAt: "2026-07-07T00:00:00.000Z",
|
|
280
|
+
projections: { [escapeKey]: { sha: "0000000000000000", entities: ["m1"] } },
|
|
281
|
+
piVersion: "1.8.1",
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
const stubState = { phase: "idle" } as unknown as GSDState;
|
|
285
|
+
const ctx: DriftContext = { basePath: base, state: stubState };
|
|
286
|
+
|
|
287
|
+
// Must not throw and must not emit a record referencing the sentinel.
|
|
288
|
+
const drift = await externalMarkdownEditHandler.detect(stubState, ctx);
|
|
289
|
+
assert.equal(drift.length, 0, "no drift record from a rejected hostile marker");
|
|
290
|
+
assert.ok(
|
|
291
|
+
!drift.some((d) => d.projectionPath.includes(sentinelName)),
|
|
292
|
+
"detector must not reference the out-of-project sentinel",
|
|
293
|
+
);
|
|
294
|
+
});
|