@opengsd/gsd-pi 1.1.1-dev.616a1a1 → 1.1.1-dev.74e8dd1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +167 -16
- package/dist/resources/extensions/gsd/auto/phases.js +4 -3
- package/dist/resources/extensions/gsd/auto-dashboard.js +15 -4
- package/dist/resources/extensions/gsd/auto-dispatch.js +39 -0
- package/dist/resources/extensions/gsd/auto-post-unit.js +113 -7
- package/dist/resources/extensions/gsd/auto-prompts.js +9 -0
- package/dist/resources/extensions/gsd/auto-recovery.js +4 -4
- package/dist/resources/extensions/gsd/auto-start.js +94 -15
- package/dist/resources/extensions/gsd/auto-unit-tool-scope.js +2 -1
- package/dist/resources/extensions/gsd/auto.js +22 -4
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +79 -0
- package/dist/resources/extensions/gsd/bootstrap/exec-tools.js +43 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +30 -9
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +16 -10
- package/dist/resources/extensions/gsd/commands/catalog.js +6 -1
- package/dist/resources/extensions/gsd/commands/handlers/core.js +6 -2
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +7 -3
- package/dist/resources/extensions/gsd/commands-maintenance.js +172 -2
- package/dist/resources/extensions/gsd/commands-mcp-status.js +107 -59
- package/dist/resources/extensions/gsd/commands-prefs-wizard.js +3 -1
- package/dist/resources/extensions/gsd/commands-verdict.js +1 -1
- package/dist/resources/extensions/gsd/config-overlay.js +2 -1
- package/dist/resources/extensions/gsd/error-classifier.js +2 -1
- package/dist/resources/extensions/gsd/exec-sandbox.js +2 -0
- package/dist/resources/extensions/gsd/gsd-db.js +37 -4
- package/dist/resources/extensions/gsd/guided-flow.js +1 -1
- package/dist/resources/extensions/gsd/mcp-filter.js +3 -0
- package/dist/resources/extensions/gsd/mcp-project-config.js +67 -8
- package/dist/resources/extensions/gsd/migration-auto-check.js +2 -2
- package/dist/resources/extensions/gsd/prompts/run-uat.md +10 -4
- package/dist/resources/extensions/gsd/prompts/system.md +3 -1
- package/dist/resources/extensions/gsd/safety/destructive-guard.js +3 -0
- package/dist/resources/extensions/gsd/skill-activation.js +20 -3
- package/dist/resources/extensions/gsd/state-reconciliation/drift/artifact-db.js +4 -2
- package/dist/resources/extensions/gsd/state-reconciliation/drift/project-md.js +1 -1
- package/dist/resources/extensions/gsd/state-reconciliation/drift/roadmap.js +18 -1
- package/dist/resources/extensions/gsd/state-reconciliation/index.js +6 -0
- package/dist/resources/extensions/gsd/state.js +15 -12
- package/dist/resources/extensions/gsd/tool-presentation-plan.js +120 -0
- package/dist/resources/extensions/gsd/tools/exec-tool.js +109 -0
- package/dist/resources/extensions/gsd/tools/plan-slice.js +14 -9
- package/dist/resources/extensions/gsd/tools/reopen-milestone.js +2 -2
- package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +366 -3
- package/dist/resources/extensions/gsd/unit-context-manifest.js +8 -3
- package/dist/resources/extensions/gsd/validation-block-guard.js +2 -0
- package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +3 -1
- package/dist/resources/extensions/gsd/workflow-mcp.js +5 -1
- package/dist/resources/extensions/gsd/worktree-lifecycle.js +24 -0
- package/dist/resources/extensions/mcp-client/manager.js +31 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +4 -4
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +4 -4
- 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/server/server-reference-manifest.json +1 -1
- package/package.json +2 -2
- 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/package.json +4 -4
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts +2 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js +10 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/components/settings-selector.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js +72 -31
- package/packages/gsd-agent-modes/dist/modes/interactive/controllers/chat-controller.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-dialogs.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-dialogs.js +2 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-extension-dialogs.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode-class-constants.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js +1 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.d.ts.map +1 -1
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js +5 -0
- package/packages/gsd-agent-modes/dist/modes/interactive/interactive-selectors-settings.js.map +1 -1
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.js +82 -0
- package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
- package/packages/mcp-server/package.json +3 -3
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/image-models.generated.d.ts +15 -0
- package/packages/pi-ai/dist/image-models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/image-models.generated.js +15 -0
- package/packages/pi-ai/dist/image-models.generated.js.map +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +338 -17
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +412 -112
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +11 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/dist/terminal.d.ts +1 -0
- package/packages/pi-tui/dist/terminal.d.ts.map +1 -1
- package/packages/pi-tui/dist/terminal.js +8 -4
- package/packages/pi-tui/dist/terminal.js.map +1 -1
- package/packages/pi-tui/package.json +1 -1
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +196 -16
- package/src/resources/extensions/claude-code-cli/tests/stream-adapter.test.ts +239 -63
- package/src/resources/extensions/gsd/auto/phases.ts +5 -3
- package/src/resources/extensions/gsd/auto-dashboard.ts +16 -4
- package/src/resources/extensions/gsd/auto-dispatch.ts +48 -0
- package/src/resources/extensions/gsd/auto-post-unit.ts +138 -7
- package/src/resources/extensions/gsd/auto-prompts.ts +9 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +4 -4
- package/src/resources/extensions/gsd/auto-start.ts +112 -17
- package/src/resources/extensions/gsd/auto-unit-tool-scope.ts +2 -1
- package/src/resources/extensions/gsd/auto.ts +35 -3
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +86 -0
- package/src/resources/extensions/gsd/bootstrap/exec-tools.ts +51 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +51 -14
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +21 -10
- package/src/resources/extensions/gsd/commands/catalog.ts +6 -1
- package/src/resources/extensions/gsd/commands/handlers/core.ts +6 -2
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +7 -3
- package/src/resources/extensions/gsd/commands-maintenance.ts +197 -2
- package/src/resources/extensions/gsd/commands-mcp-status.ts +134 -57
- package/src/resources/extensions/gsd/commands-prefs-wizard.ts +4 -1
- package/src/resources/extensions/gsd/commands-verdict.ts +1 -1
- package/src/resources/extensions/gsd/config-overlay.ts +3 -1
- package/src/resources/extensions/gsd/error-classifier.ts +2 -1
- package/src/resources/extensions/gsd/exec-sandbox.ts +4 -0
- package/src/resources/extensions/gsd/gsd-db.ts +41 -6
- package/src/resources/extensions/gsd/guided-flow.ts +1 -1
- package/src/resources/extensions/gsd/mcp-filter.ts +3 -0
- package/src/resources/extensions/gsd/mcp-project-config.ts +92 -10
- package/src/resources/extensions/gsd/migration-auto-check.ts +2 -2
- package/src/resources/extensions/gsd/preferences-types.ts +1 -1
- package/src/resources/extensions/gsd/prompts/run-uat.md +10 -4
- package/src/resources/extensions/gsd/prompts/system.md +3 -1
- package/src/resources/extensions/gsd/safety/destructive-guard.ts +3 -0
- package/src/resources/extensions/gsd/skill-activation.ts +20 -2
- package/src/resources/extensions/gsd/state-reconciliation/drift/artifact-db.ts +4 -2
- package/src/resources/extensions/gsd/state-reconciliation/drift/project-md.ts +1 -1
- package/src/resources/extensions/gsd/state-reconciliation/drift/roadmap.ts +20 -0
- package/src/resources/extensions/gsd/state-reconciliation/index.ts +6 -0
- package/src/resources/extensions/gsd/state-reconciliation/types.ts +1 -0
- package/src/resources/extensions/gsd/state.ts +16 -12
- package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +51 -0
- package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +86 -0
- package/src/resources/extensions/gsd/tests/auto-start-orphan-bootstrap.test.ts +143 -2
- package/src/resources/extensions/gsd/tests/auto-start-project-milestone-reconcile.test.ts +24 -2
- package/src/resources/extensions/gsd/tests/commands-dispatcher-validation-block.test.ts +38 -3
- package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +6 -2
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +50 -13
- package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +60 -0
- package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +18 -0
- package/src/resources/extensions/gsd/tests/exec-tool.test.ts +69 -0
- package/src/resources/extensions/gsd/tests/gsd-rebuild.test.ts +199 -0
- package/src/resources/extensions/gsd/tests/gsd-recover.test.ts +75 -0
- package/src/resources/extensions/gsd/tests/integration/state-machine-live-validation.test.ts +13 -6
- package/src/resources/extensions/gsd/tests/mcp-filter.test.ts +15 -0
- package/src/resources/extensions/gsd/tests/mcp-project-config.test.ts +68 -0
- package/src/resources/extensions/gsd/tests/mcp-status.test.ts +177 -0
- package/src/resources/extensions/gsd/tests/migration-auto-check.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/parallel-skill-prompt-integration.test.ts +54 -7
- package/src/resources/extensions/gsd/tests/plan-slice.test.ts +39 -1
- package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +18 -1
- package/src/resources/extensions/gsd/tests/reactive-executor.test.ts +36 -0
- package/src/resources/extensions/gsd/tests/register-hooks-depth-verification.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/restore-tools-after-discuss.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/skill-activation.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/start-auto-detached.test.ts +6 -2
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +52 -0
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +84 -10
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +12 -2
- package/src/resources/extensions/gsd/tests/tui-header-lifecycle.test.ts +29 -6
- package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +29 -6
- package/src/resources/extensions/gsd/tests/validation-block-guard.test.ts +21 -0
- package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +17 -2
- package/src/resources/extensions/gsd/tests/workflow-tool-executors.test.ts +83 -0
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +25 -0
- package/src/resources/extensions/gsd/tool-presentation-plan.ts +167 -0
- package/src/resources/extensions/gsd/tools/exec-tool.ts +130 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +14 -9
- package/src/resources/extensions/gsd/tools/reopen-milestone.ts +2 -2
- package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +440 -2
- package/src/resources/extensions/gsd/unit-context-manifest.ts +14 -5
- package/src/resources/extensions/gsd/validation-block-guard.ts +2 -0
- package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +2 -1
- package/src/resources/extensions/gsd/workflow-mcp.ts +5 -1
- package/src/resources/extensions/gsd/worktree-lifecycle.ts +26 -0
- package/src/resources/extensions/mcp-client/manager.ts +33 -1
- package/src/resources/extensions/mcp-client/tests/manager.test.ts +35 -0
- /package/dist/web/standalone/.next/static/{L9N5SPFi7f-Ne4u2uXzCe → eRWf-RI9bzbrwEurm_3uI}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{L9N5SPFi7f-Ne4u2uXzCe → eRWf-RI9bzbrwEurm_3uI}/_ssgManifest.js +0 -0
|
@@ -37,7 +37,13 @@ You are the UAT runner. Execute every check defined in `{{uatPath}}` as deeply a
|
|
|
37
37
|
|
|
38
38
|
Choose the lightest tool that proves the check honestly:
|
|
39
39
|
|
|
40
|
-
- Run
|
|
40
|
+
- Run automated checks with `gsd_uat_exec`
|
|
41
|
+
- Use `uat-artifact-check` as `intent` for static file, grep, structure, or artifact checks.
|
|
42
|
+
- Use `uat-runtime-check` as `intent` for executing tests, scripts, or runtime assertions.
|
|
43
|
+
- Use `uat-browser-check` as `intent` for browser interaction or screenshot-backed UI checks.
|
|
44
|
+
- Use `uat-service-start` as `intent` only when starting or connecting to an app/service.
|
|
45
|
+
- Use `uat-log-inspection` as `intent` for checking logs or captured output files.
|
|
46
|
+
- The result-table evidence mode is separate; do not use `artifact`, `runtime`, or `human-follow-up` as `intent`.
|
|
41
47
|
- Run `grep` / `rg` checks against files
|
|
42
48
|
- Run `node` / other script invocations
|
|
43
49
|
- Read files and verify their contents
|
|
@@ -48,7 +54,7 @@ Choose the lightest tool that proves the check honestly:
|
|
|
48
54
|
For each check, record:
|
|
49
55
|
- The check description (from the UAT file)
|
|
50
56
|
- The evidence mode used: `artifact`, `runtime`, or `human-follow-up`
|
|
51
|
-
- The command or action taken
|
|
57
|
+
- The command or action taken, including the `gsd_uat_exec` evidence ID for automated checks
|
|
52
58
|
- The actual result observed
|
|
53
59
|
- `PASS`, `FAIL`, or `NEEDS-HUMAN`
|
|
54
60
|
|
|
@@ -57,7 +63,7 @@ After running all checks, compute the **overall verdict**:
|
|
|
57
63
|
- `FAIL` — one or more automatable checks failed
|
|
58
64
|
- `PARTIAL` — one or more automatable checks were skipped or returned inconclusive results (not the same as `NEEDS-HUMAN` — use PARTIAL only when the agent itself could not determine pass/fail for a check it was supposed to automate)
|
|
59
65
|
|
|
60
|
-
Call `gsd_summary_save` with `milestone_id: {{milestoneId}}`, `slice_id: {{sliceId}}`, `artifact_type: "ASSESSMENT"`, and the full UAT result markdown as `content
|
|
66
|
+
Call `gsd_summary_save` with `milestone_id: "{{milestoneId}}"`, `slice_id: "{{sliceId}}"`, `artifact_type: "ASSESSMENT"`, and the full UAT result markdown as `content`. The tool computes the assessment path, persists to DB/disk, and saves the aggregate UAT gate. The content should follow this logical shape:
|
|
61
67
|
|
|
62
68
|
```markdown
|
|
63
69
|
---
|
|
@@ -86,6 +92,6 @@ date: <ISO 8601 timestamp>
|
|
|
86
92
|
|
|
87
93
|
---
|
|
88
94
|
|
|
89
|
-
**You MUST call `gsd_summary_save` with the UAT result content before finishing.**
|
|
95
|
+
**You MUST call `gsd_summary_save` with `artifact_type: "ASSESSMENT"` and the UAT result content before finishing. Do not write the assessment file directly.**
|
|
90
96
|
|
|
91
97
|
When done, say: "UAT {{sliceId}} complete."
|
|
@@ -32,7 +32,7 @@ GSD ships with bundled skills. Installed skills are listed in `<available_skills
|
|
|
32
32
|
- Never print, echo, log, or restate secrets or credentials. Report only key names and applied/skipped status.
|
|
33
33
|
- Never ask the user to edit `.env` files or set secrets manually. Use `secure_env_collect`.
|
|
34
34
|
- In enduring files, write current state only unless the file is explicitly historical.
|
|
35
|
-
- **Never take outward-facing actions on GitHub or external services without explicit user confirmation.** This includes creating/closing issues, merging/approving/commenting on PRs, pushing remote branches, publishing packages, or any state change outside local filesystem. Read-only listing/viewing/diffing is fine. Present intent and get a clear "yes" first. **Non-bypassable:** no response, ambiguity, or `ask_user_questions` failure means re-ask; never rationalize past the block. Missing "yes" means "no."
|
|
35
|
+
- **Never take outward-facing actions on GitHub or external services without explicit user confirmation.** This includes creating/closing issues, merging/approving/commenting on PRs, pushing remote branches, publishing packages, terragrunt/aws/kubectl mutations, or any state change outside local filesystem. Read-only listing/viewing/diffing is fine. Present intent and get a clear "yes" first. **Non-bypassable:** no response, ambiguity, or `ask_user_questions` failure means re-ask; never rationalize past the block. Missing "yes" means "no."
|
|
36
36
|
|
|
37
37
|
If a `GSD Skill Preferences` block appears below, treat it as durable guidance for skills to use, prefer, or avoid unless it conflicts with artifact rules, verification, or higher-priority instructions.
|
|
38
38
|
|
|
@@ -160,4 +160,6 @@ Fix root causes, not symptoms. If applying temporary mitigation, label it and pr
|
|
|
160
160
|
- When debugging, stay curious. Problems are puzzles. Say what's interesting about the failure before reaching for fixes.
|
|
161
161
|
- After completing a task, give a brief summary and 2-4 numbered next-step options; last option is always "Other". Omit the list for strict output formats.
|
|
162
162
|
|
|
163
|
+
If any next step is destructive/outward-facing, present it via `ask_user_questions` and wait for the user's answer before execution. Do not execute a next-step item from a prior plain-text numbered list without fresh confirmation.
|
|
164
|
+
|
|
163
165
|
Good narration states a decision or finding: "Three handlers follow a middleware pattern - using that instead of a custom wrapper." Bad narration just announces the next call ("Reading the file now.") or emits compressed planner notes ("Need create plan artifact maybe read existing plans.").
|
|
@@ -24,6 +24,9 @@ const DESTRUCTIVE_PATTERNS: readonly DestructivePattern[] = [
|
|
|
24
24
|
{ pattern: /\btruncate\s+table\b/i, label: "SQL truncate" },
|
|
25
25
|
{ pattern: /\bchmod\s+777\b/, label: "world-writable permissions" },
|
|
26
26
|
{ pattern: /\bcurl\s.*\|\s*(bash|sh|zsh)\b/, label: "pipe to shell" },
|
|
27
|
+
{ pattern: /\bterra(form|grunt)\s+(apply|destroy)/i, label: "IaC apply/destroy" },
|
|
28
|
+
{ pattern: /\baws\s+\w+\s+(delete|create|put|remove|terminate)\b/i, label: "AWS mutation" },
|
|
29
|
+
{ pattern: /\bkubectl\s+(delete|apply)\b/i, label: "kubectl mutation" },
|
|
27
30
|
];
|
|
28
31
|
|
|
29
32
|
// ─── Public API ─────────────────────────────────────────────────────────────
|
|
@@ -50,6 +50,16 @@ function tokenizeSkillContext(...parts: Array<string | null | undefined>): Set<s
|
|
|
50
50
|
return tokens;
|
|
51
51
|
}
|
|
52
52
|
|
|
53
|
+
function tokenizeUnitType(unitType: string | undefined): Set<string> {
|
|
54
|
+
const tokens = new Set<string>();
|
|
55
|
+
const value = unitType?.trim().toLowerCase();
|
|
56
|
+
if (!value) return tokens;
|
|
57
|
+
tokens.add(value);
|
|
58
|
+
tokens.add(value.replace(/[-_]+/g, " "));
|
|
59
|
+
tokens.add(value.replace(/[-_\s]+/g, ""));
|
|
60
|
+
return tokens;
|
|
61
|
+
}
|
|
62
|
+
|
|
53
63
|
function skillMatchesContext(skill: Skill, contextTokens: Set<string>): boolean {
|
|
54
64
|
const haystacks = [
|
|
55
65
|
skill.name.toLowerCase(),
|
|
@@ -79,17 +89,25 @@ function ruleMatchesContext(when: string, contextTokens: Set<string>): boolean {
|
|
|
79
89
|
);
|
|
80
90
|
}
|
|
81
91
|
|
|
92
|
+
function ruleMatchesUnitType(when: string, unitType: string | undefined): boolean {
|
|
93
|
+
if (!unitType) return false;
|
|
94
|
+
const whenTokens = tokenizeSkillContext(when);
|
|
95
|
+
const unitTokens = tokenizeUnitType(unitType);
|
|
96
|
+
return [...unitTokens].some(token => whenTokens.has(token));
|
|
97
|
+
}
|
|
98
|
+
|
|
82
99
|
function resolveSkillRuleMatches(
|
|
83
100
|
prefs: GSDPreferences | undefined,
|
|
84
101
|
contextTokens: Set<string>,
|
|
85
102
|
base: string,
|
|
103
|
+
unitType?: string,
|
|
86
104
|
): { include: string[]; avoid: string[] } {
|
|
87
105
|
if (!prefs?.skill_rules?.length) return { include: [], avoid: [] };
|
|
88
106
|
|
|
89
107
|
const include: string[] = [];
|
|
90
108
|
const avoid: string[] = [];
|
|
91
109
|
for (const rule of prefs.skill_rules) {
|
|
92
|
-
if (!ruleMatchesContext(rule.when, contextTokens)) continue;
|
|
110
|
+
if (!ruleMatchesContext(rule.when, contextTokens) && !ruleMatchesUnitType(rule.when, unitType)) continue;
|
|
93
111
|
include.push(...resolvePreferenceSkillNames([...(rule.use ?? []), ...(rule.prefer ?? [])], base));
|
|
94
112
|
avoid.push(...resolvePreferenceSkillNames(rule.avoid ?? [], base));
|
|
95
113
|
}
|
|
@@ -196,7 +214,7 @@ export function buildSkillActivationBlock(params: {
|
|
|
196
214
|
matched.add(name);
|
|
197
215
|
}
|
|
198
216
|
|
|
199
|
-
const ruleMatches = resolveSkillRuleMatches(prefs, contextTokens, params.base);
|
|
217
|
+
const ruleMatches = resolveSkillRuleMatches(prefs, contextTokens, params.base, params.unitType);
|
|
200
218
|
for (const name of ruleMatches.include) matched.add(name);
|
|
201
219
|
for (const name of ruleMatches.avoid) avoided.add(name);
|
|
202
220
|
|
|
@@ -434,7 +434,8 @@ export function repairArtifactDbDrift(
|
|
|
434
434
|
`Artifact/DB status drift in ${record.milestoneId}` +
|
|
435
435
|
`${record.sliceId ? `/${record.sliceId}` : ""}` +
|
|
436
436
|
`${record.taskId ? `/${record.taskId}` : ""}: ${record.reason}. ` +
|
|
437
|
-
"Runtime will not silently import completion artifacts into DB state
|
|
437
|
+
"Runtime will not silently import completion artifacts into DB state. " +
|
|
438
|
+
"Run `/gsd rebuild markdown` after review to quarantine stale projections and re-render from the DB; use `/gsd recover --confirm` only when markdown should repopulate a lost or corrupt DB.",
|
|
438
439
|
);
|
|
439
440
|
}
|
|
440
441
|
|
|
@@ -461,7 +462,8 @@ export function describeArtifactDbDriftBlocker(
|
|
|
461
462
|
`Artifact/DB status drift in ${record.milestoneId}` +
|
|
462
463
|
`${record.sliceId ? `/${record.sliceId}` : ""}` +
|
|
463
464
|
`${record.taskId ? `/${record.taskId}` : ""}: ${record.reason}. ` +
|
|
464
|
-
"Runtime will not silently import completion artifacts into DB state
|
|
465
|
+
"Runtime will not silently import completion artifacts into DB state. " +
|
|
466
|
+
"Run `/gsd rebuild markdown` after review to quarantine stale projections and re-render from the DB; use `/gsd recover --confirm` only when markdown should repopulate a lost or corrupt DB."
|
|
465
467
|
);
|
|
466
468
|
}
|
|
467
469
|
|
|
@@ -55,7 +55,7 @@ export function repairUnregisteredMilestone(
|
|
|
55
55
|
): void {
|
|
56
56
|
throw new Error(
|
|
57
57
|
`Milestone ${record.milestoneId} exists only as markdown projection. ` +
|
|
58
|
-
"Runtime reconciliation will not import markdown into the authoritative DB; run `/gsd recover` if this markdown should repopulate the database.",
|
|
58
|
+
"Runtime reconciliation will not import markdown into the authoritative DB; run `/gsd recover --confirm` if this markdown should repopulate the database.",
|
|
59
59
|
);
|
|
60
60
|
}
|
|
61
61
|
|
|
@@ -9,6 +9,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
9
9
|
import {
|
|
10
10
|
getMilestone,
|
|
11
11
|
getMilestoneSlices,
|
|
12
|
+
getSliceTasks,
|
|
12
13
|
isDbAvailable,
|
|
13
14
|
} from "../../gsd-db.js";
|
|
14
15
|
import { renderRoadmapFromDb } from "../../markdown-renderer.js";
|
|
@@ -30,6 +31,19 @@ function arraysEqual(a: readonly string[], b: readonly string[]): boolean {
|
|
|
30
31
|
return true;
|
|
31
32
|
}
|
|
32
33
|
|
|
34
|
+
function getSlicesReadyForDivergenceCheck(
|
|
35
|
+
milestoneId: string,
|
|
36
|
+
dbSlices: ReturnType<typeof getMilestoneSlices>,
|
|
37
|
+
): Set<string> {
|
|
38
|
+
const ready = new Set<string>();
|
|
39
|
+
for (const slice of dbSlices) {
|
|
40
|
+
if (isClosedStatus(slice.status) || getSliceTasks(milestoneId, slice.id).length > 0) {
|
|
41
|
+
ready.add(slice.id);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return ready;
|
|
45
|
+
}
|
|
46
|
+
|
|
33
47
|
function milestoneHasDivergence(
|
|
34
48
|
basePath: string,
|
|
35
49
|
milestoneId: string,
|
|
@@ -46,6 +60,10 @@ function milestoneHasDivergence(
|
|
|
46
60
|
|
|
47
61
|
const dbSlices = getMilestoneSlices(milestoneId);
|
|
48
62
|
const dbSliceMap = new Map(dbSlices.map((s) => [s.id, s]));
|
|
63
|
+
const readySliceIds = getSlicesReadyForDivergenceCheck(milestoneId, dbSlices);
|
|
64
|
+
if (dbSlices.length > 0 && readySliceIds.size === 0) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
49
67
|
const roadmapSliceIds = new Set<string>();
|
|
50
68
|
|
|
51
69
|
for (let i = 0; i < roadmap.slices.length; i++) {
|
|
@@ -54,11 +72,13 @@ function milestoneHasDivergence(
|
|
|
54
72
|
const expectedSequence = i + 1;
|
|
55
73
|
const dbSlice = dbSliceMap.get(roadmapSlice.id);
|
|
56
74
|
if (!dbSlice) return true; // Roadmap has a slice the DB doesn't.
|
|
75
|
+
if (!readySliceIds.has(dbSlice.id)) continue;
|
|
57
76
|
if (dbSlice.sequence !== expectedSequence) return true;
|
|
58
77
|
if (!arraysEqual(dbSlice.depends, roadmapSlice.depends)) return true;
|
|
59
78
|
if (isClosedStatus(dbSlice.status) !== roadmapSlice.done) return true;
|
|
60
79
|
}
|
|
61
80
|
for (const dbSlice of dbSlices) {
|
|
81
|
+
if (!readySliceIds.has(dbSlice.id)) continue;
|
|
62
82
|
if (!roadmapSliceIds.has(dbSlice.id)) return true;
|
|
63
83
|
}
|
|
64
84
|
return false;
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
deriveState as defaultDeriveState,
|
|
7
7
|
invalidateStateCache as defaultInvalidate,
|
|
8
8
|
} from "../state.js";
|
|
9
|
+
import { clearParseCache as defaultClearParseCache } from "../files.js";
|
|
9
10
|
import type { GSDState } from "../types.js";
|
|
10
11
|
|
|
11
12
|
import {
|
|
@@ -37,6 +38,7 @@ const MAX_PASSES = 2;
|
|
|
37
38
|
const defaultDeps: ReconciliationDeps = {
|
|
38
39
|
invalidateStateCache: defaultInvalidate,
|
|
39
40
|
deriveState: defaultDeriveState,
|
|
41
|
+
clearParseCache: defaultClearParseCache,
|
|
40
42
|
};
|
|
41
43
|
|
|
42
44
|
/**
|
|
@@ -58,6 +60,7 @@ export async function reconcileBeforeDispatch(
|
|
|
58
60
|
deps: ReconciliationDeps = defaultDeps,
|
|
59
61
|
): Promise<ReconciliationResult> {
|
|
60
62
|
const registry = deps.registry ?? DRIFT_REGISTRY;
|
|
63
|
+
const clearParseCache = deps.clearParseCache ?? defaultClearParseCache;
|
|
61
64
|
const repaired: DriftRecord[] = [];
|
|
62
65
|
|
|
63
66
|
for (let pass = 0; pass < MAX_PASSES; pass++) {
|
|
@@ -103,6 +106,9 @@ export async function reconcileBeforeDispatch(
|
|
|
103
106
|
}
|
|
104
107
|
}
|
|
105
108
|
|
|
109
|
+
if (repairedThisPass) {
|
|
110
|
+
clearParseCache();
|
|
111
|
+
}
|
|
106
112
|
if (blockers.length > 0) {
|
|
107
113
|
let blockerState = stateSnapshot;
|
|
108
114
|
if (repairedThisPass) {
|
|
@@ -101,6 +101,7 @@ export interface ReconciliationDeps {
|
|
|
101
101
|
basePath: string,
|
|
102
102
|
opts?: DeriveStateOptions,
|
|
103
103
|
) => Promise<GSDState>;
|
|
104
|
+
clearParseCache?: () => void;
|
|
104
105
|
/**
|
|
105
106
|
* Override of the drift handler catalog. Defaults to DRIFT_REGISTRY. Each
|
|
106
107
|
* handler is parameterized over its own DriftRecord variant; the union of
|
|
@@ -83,7 +83,7 @@ function formatNeedsRemediationBlocker(milestoneId: string): string {
|
|
|
83
83
|
return [
|
|
84
84
|
`Milestone ${milestoneId} is blocked because milestone validation returned needs-remediation, but all slices are complete.`,
|
|
85
85
|
`Fix options:`,
|
|
86
|
-
`1.
|
|
86
|
+
`1. Run \`/gsd dispatch reassess\` to add remediation slices, then run \`/gsd auto\``,
|
|
87
87
|
`2. If the finding is acceptable, override it: \`/gsd verdict pass --rationale "why this is okay"\``,
|
|
88
88
|
`3. If this should wait, defer it explicitly: \`/gsd park ${milestoneId}\``,
|
|
89
89
|
].join("\n");
|
|
@@ -279,10 +279,17 @@ export function invalidateStateCache(): void {
|
|
|
279
279
|
/**
|
|
280
280
|
* Returns the ID of the first incomplete milestone, or null if all are complete.
|
|
281
281
|
*/
|
|
282
|
+
function getRequestedMilestoneLock(): string | undefined {
|
|
283
|
+
const lock = process.env.GSD_MILESTONE_LOCK?.trim();
|
|
284
|
+
return lock || undefined;
|
|
285
|
+
}
|
|
286
|
+
|
|
282
287
|
export async function getActiveMilestoneId(basePath: string): Promise<string | null> {
|
|
283
|
-
//
|
|
284
|
-
//
|
|
285
|
-
|
|
288
|
+
// Milestone-scoped execution. Parallel workers and explicit solo commands
|
|
289
|
+
// such as `/gsd auto M002` both set GSD_MILESTONE_LOCK; state derivation must
|
|
290
|
+
// honor it so recovery/adoption sees the requested milestone, not the first
|
|
291
|
+
// open milestone in queue order.
|
|
292
|
+
const milestoneLock = getRequestedMilestoneLock();
|
|
286
293
|
if (milestoneLock) {
|
|
287
294
|
if (isDbAvailable()) {
|
|
288
295
|
const locked = getAllMilestones().find(m => m.id === milestoneLock);
|
|
@@ -720,7 +727,7 @@ export async function deriveStateFromDb(
|
|
|
720
727
|
|
|
721
728
|
const allMilestones = getAllMilestones();
|
|
722
729
|
|
|
723
|
-
const milestoneLock =
|
|
730
|
+
const milestoneLock = getRequestedMilestoneLock();
|
|
724
731
|
const milestones = milestoneLock
|
|
725
732
|
? allMilestones.filter(m => m.id === milestoneLock)
|
|
726
733
|
: allMilestones;
|
|
@@ -959,13 +966,10 @@ export async function _deriveStateImpl(
|
|
|
959
966
|
const customOrder = loadQueueOrder(basePath);
|
|
960
967
|
const milestoneIds = sortByQueueOrder(diskIds, customOrder);
|
|
961
968
|
|
|
962
|
-
// ──
|
|
963
|
-
//
|
|
964
|
-
//
|
|
965
|
-
|
|
966
|
-
// don't exist). This gives each worker complete isolation without
|
|
967
|
-
// modifying any other state derivation logic.
|
|
968
|
-
const milestoneLock = process.env.GSD_PARALLEL_WORKER ? process.env.GSD_MILESTONE_LOCK : undefined;
|
|
969
|
+
// ── Milestone-scoped execution ─────────────────────────────────────────
|
|
970
|
+
// Parallel workers and explicit solo recovery both scope auto-mode to one
|
|
971
|
+
// milestone through GSD_MILESTONE_LOCK.
|
|
972
|
+
const milestoneLock = getRequestedMilestoneLock();
|
|
969
973
|
if (milestoneLock && milestoneIds.includes(milestoneLock)) {
|
|
970
974
|
milestoneIds.length = 0;
|
|
971
975
|
milestoneIds.push(milestoneLock);
|
|
@@ -16,11 +16,13 @@ import {
|
|
|
16
16
|
buildPhaseHandoffOutcome,
|
|
17
17
|
updateProgressWidget,
|
|
18
18
|
setAutoOutcomeWidget,
|
|
19
|
+
setAutoActiveStatus,
|
|
19
20
|
setCompletionProgressWidget,
|
|
20
21
|
getRoadmapSlicesSync,
|
|
21
22
|
clearSliceProgressCache,
|
|
22
23
|
getWidgetMode,
|
|
23
24
|
cycleWidgetMode,
|
|
25
|
+
setWidgetMode,
|
|
24
26
|
_resetWidgetModeForTests,
|
|
25
27
|
_resetLastCommitCacheForTests,
|
|
26
28
|
_refreshLastCommitForTests,
|
|
@@ -78,6 +80,26 @@ test("unitVerb handles hook types", () => {
|
|
|
78
80
|
assert.equal(unitVerb("hook/"), "hook: ");
|
|
79
81
|
});
|
|
80
82
|
|
|
83
|
+
test("setAutoActiveStatus clears stale outcome surfaces", () => {
|
|
84
|
+
const statusCalls: Array<[string, string]> = [];
|
|
85
|
+
const widgetCalls: Array<[string, unknown]> = [];
|
|
86
|
+
|
|
87
|
+
setAutoActiveStatus({
|
|
88
|
+
hasUI: true,
|
|
89
|
+
ui: {
|
|
90
|
+
setStatus: (key: string, value: string) => {
|
|
91
|
+
statusCalls.push([key, value]);
|
|
92
|
+
},
|
|
93
|
+
setWidget: (key: string, value: unknown) => {
|
|
94
|
+
widgetCalls.push([key, value]);
|
|
95
|
+
},
|
|
96
|
+
},
|
|
97
|
+
} as any, "next");
|
|
98
|
+
|
|
99
|
+
assert.deepEqual(statusCalls, [["gsd-auto", "next"]]);
|
|
100
|
+
assert.deepEqual(widgetCalls, [["gsd-outcome", undefined]]);
|
|
101
|
+
});
|
|
102
|
+
|
|
81
103
|
// ─── unitPhaseLabel ───────────────────────────────────────────────────────
|
|
82
104
|
|
|
83
105
|
test("unitPhaseLabel maps known types to labels", () => {
|
|
@@ -571,14 +593,21 @@ test("updateProgressWidget refreshes slice progress cache immediately", (t) => {
|
|
|
571
593
|
test("updateProgressWidget full mode keeps footer-owned signals out of auto deck", (t) => {
|
|
572
594
|
const dir = makeTempDir("command-deck");
|
|
573
595
|
mkdirSync(join(dir, ".gsd"), { recursive: true });
|
|
596
|
+
const projectPrefsPath = join(dir, ".gsd", "preferences.md");
|
|
597
|
+
const globalPrefsPath = join(dir, ".gsd", "global-preferences.md");
|
|
598
|
+
writeFileSync(projectPrefsPath, "---\nversion: 1\n---\n", "utf-8");
|
|
574
599
|
let widget: { render(width: number): string[]; dispose?: () => void } | null = null;
|
|
575
600
|
|
|
576
601
|
t.after(() => {
|
|
577
602
|
widget?.dispose?.();
|
|
603
|
+
_resetWidgetModeForTests();
|
|
578
604
|
clearSliceProgressCache();
|
|
579
605
|
cleanup(dir);
|
|
580
606
|
});
|
|
581
607
|
|
|
608
|
+
_resetWidgetModeForTests();
|
|
609
|
+
setWidgetMode("full", projectPrefsPath, globalPrefsPath);
|
|
610
|
+
|
|
582
611
|
updateProgressWidget(
|
|
583
612
|
{
|
|
584
613
|
hasUI: true,
|
|
@@ -812,3 +841,25 @@ test("widget mode respects project preference precedence and persists there", (t
|
|
|
812
841
|
assert.match(projectPrefs, /widget_mode:\s*min/);
|
|
813
842
|
assert.match(globalPrefs, /widget_mode:\s*off/);
|
|
814
843
|
});
|
|
844
|
+
|
|
845
|
+
test("widget mode defaults to small when preferences do not set it", (t) => {
|
|
846
|
+
const homeDir = makeTempDir("home-no-widget-pref");
|
|
847
|
+
const projectDir = makeTempDir("project-no-widget-pref");
|
|
848
|
+
const globalPrefsPath = join(homeDir, ".gsd", "preferences.md");
|
|
849
|
+
const projectPrefsPath = join(projectDir, ".gsd", "preferences.md");
|
|
850
|
+
|
|
851
|
+
mkdirSync(join(homeDir, ".gsd"), { recursive: true });
|
|
852
|
+
mkdirSync(join(projectDir, ".gsd"), { recursive: true });
|
|
853
|
+
writeFileSync(globalPrefsPath, "---\nversion: 1\n---\n", "utf-8");
|
|
854
|
+
writeFileSync(projectPrefsPath, "---\nversion: 1\n---\n", "utf-8");
|
|
855
|
+
|
|
856
|
+
t.after(() => {
|
|
857
|
+
cleanup(homeDir);
|
|
858
|
+
cleanup(projectDir);
|
|
859
|
+
_resetWidgetModeForTests();
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
_resetWidgetModeForTests();
|
|
863
|
+
|
|
864
|
+
assert.equal(getWidgetMode(projectPrefsPath, globalPrefsPath), "small");
|
|
865
|
+
});
|
|
@@ -1246,6 +1246,92 @@ test("wired DispatchAdapter forwards constructor session when advance input omit
|
|
|
1246
1246
|
}
|
|
1247
1247
|
});
|
|
1248
1248
|
|
|
1249
|
+
test("wired DispatchAdapter adopts next active milestone after the session milestone is closed", async (t) => {
|
|
1250
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-orchestrator-milestone-adopt-"));
|
|
1251
|
+
t.after(() => rmSync(base, { recursive: true, force: true }));
|
|
1252
|
+
|
|
1253
|
+
const stateSnapshot: GSDState = {
|
|
1254
|
+
...makeState(),
|
|
1255
|
+
activeMilestone: { id: "M002", title: "Next" },
|
|
1256
|
+
registry: [
|
|
1257
|
+
{ id: "M001", title: "First", status: "complete" },
|
|
1258
|
+
{ id: "M002", title: "Next", status: "active" },
|
|
1259
|
+
],
|
|
1260
|
+
};
|
|
1261
|
+
const captured: DispatchContext[] = [];
|
|
1262
|
+
const captureRule: UnifiedRule = {
|
|
1263
|
+
name: "test-milestone-adoption",
|
|
1264
|
+
when: "dispatch",
|
|
1265
|
+
evaluation: "first-match",
|
|
1266
|
+
where: async (ctx: DispatchContext) => {
|
|
1267
|
+
captured.push(ctx);
|
|
1268
|
+
return {
|
|
1269
|
+
action: "dispatch" as const,
|
|
1270
|
+
unitType: "execute-task",
|
|
1271
|
+
unitId: "M002/S01/T01",
|
|
1272
|
+
prompt: "adopted-milestone-fixture",
|
|
1273
|
+
};
|
|
1274
|
+
},
|
|
1275
|
+
then: (r: unknown) => r,
|
|
1276
|
+
};
|
|
1277
|
+
setRegistry(new RuleRegistry([captureRule]));
|
|
1278
|
+
|
|
1279
|
+
try {
|
|
1280
|
+
const ctx = { model: {}, modelRegistry: { getAll: () => [] } } as any;
|
|
1281
|
+
const pi = { getActiveTools: () => [] } as any;
|
|
1282
|
+
const session = {
|
|
1283
|
+
basePath: base,
|
|
1284
|
+
originalBasePath: base,
|
|
1285
|
+
currentMilestoneId: "M001",
|
|
1286
|
+
} as any;
|
|
1287
|
+
const adapter = createWiredDispatchAdapter(ctx, pi, base, session);
|
|
1288
|
+
|
|
1289
|
+
const result = await adapter.decideNextUnit({ stateSnapshot });
|
|
1290
|
+
|
|
1291
|
+
assert.ok(result);
|
|
1292
|
+
if (!("unitType" in result)) assert.fail(`expected dispatch decision, got ${JSON.stringify(result)}`);
|
|
1293
|
+
assert.equal(result.unitId, "M002/S01/T01");
|
|
1294
|
+
assert.equal(session.currentMilestoneId, "M002");
|
|
1295
|
+
assert.equal(captured[0]?.session?.currentMilestoneId, "M002");
|
|
1296
|
+
} finally {
|
|
1297
|
+
resetRegistry();
|
|
1298
|
+
}
|
|
1299
|
+
});
|
|
1300
|
+
|
|
1301
|
+
test("wired DispatchAdapter keeps blocking stale milestone worktree scope", async (t) => {
|
|
1302
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-orchestrator-worktree-block-"));
|
|
1303
|
+
t.after(() => rmSync(base, { recursive: true, force: true }));
|
|
1304
|
+
|
|
1305
|
+
const stateSnapshot: GSDState = {
|
|
1306
|
+
...makeState(),
|
|
1307
|
+
activeMilestone: { id: "M002", title: "Next" },
|
|
1308
|
+
registry: [
|
|
1309
|
+
{ id: "M001", title: "First", status: "complete" },
|
|
1310
|
+
{ id: "M002", title: "Next", status: "active" },
|
|
1311
|
+
],
|
|
1312
|
+
};
|
|
1313
|
+
const worktreePath = join(base, ".gsd", "worktrees", "M001");
|
|
1314
|
+
mkdirSync(worktreePath, { recursive: true });
|
|
1315
|
+
const ctx = { model: {}, modelRegistry: { getAll: () => [] } } as any;
|
|
1316
|
+
const pi = { getActiveTools: () => [] } as any;
|
|
1317
|
+
const session = {
|
|
1318
|
+
basePath: worktreePath,
|
|
1319
|
+
originalBasePath: base,
|
|
1320
|
+
currentMilestoneId: "M001",
|
|
1321
|
+
} as any;
|
|
1322
|
+
const adapter = createWiredDispatchAdapter(ctx, pi, base, session);
|
|
1323
|
+
|
|
1324
|
+
const result = await adapter.decideNextUnit({ stateSnapshot });
|
|
1325
|
+
|
|
1326
|
+
assert.deepEqual(result, {
|
|
1327
|
+
kind: "blocked",
|
|
1328
|
+
reason:
|
|
1329
|
+
'Dispatch milestone mismatch: context mid "M002" does not match session.currentMilestoneId "M001". The active worktree/session and derived project state disagree; recover, park, or discard the stranded milestone before continuing.',
|
|
1330
|
+
action: "pause",
|
|
1331
|
+
});
|
|
1332
|
+
assert.equal(session.currentMilestoneId, "M001");
|
|
1333
|
+
});
|
|
1334
|
+
|
|
1249
1335
|
test("wired DispatchAdapter replays pending verification retry dispatch", async () => {
|
|
1250
1336
|
const stateSnapshot = makeState();
|
|
1251
1337
|
const ctx = { model: {}, modelRegistry: { getAll: () => [] } } as any;
|