@opengsd/gsd-pi 1.9.0 → 1.10.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 -10
- package/dist/headless.js +2 -1
- package/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/GSD-WORKFLOW.md +2 -2
- package/dist/resources/extensions/gsd/artifact-verification.js +3 -4
- package/dist/resources/extensions/gsd/auto/loop.js +24 -4
- package/dist/resources/extensions/gsd/auto/run-unit.js +19 -1
- package/dist/resources/extensions/gsd/auto/unit-phase.js +37 -5
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +14 -8
- package/dist/resources/extensions/gsd/auto-dispatch.js +2 -0
- package/dist/resources/extensions/gsd/auto-prompts.js +102 -21
- package/dist/resources/extensions/gsd/auto-recovery.js +10 -5
- package/dist/resources/extensions/gsd/auto-runtime-state.js +5 -5
- package/dist/resources/extensions/gsd/auto-start.js +29 -9
- package/dist/resources/extensions/gsd/auto-tool-tracking.js +40 -0
- package/dist/resources/extensions/gsd/auto-worktree-cleanup.js +4 -0
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +7 -3
- package/dist/resources/extensions/gsd/auto-worktree-teardown.js +1 -1
- package/dist/resources/extensions/gsd/auto.js +15 -5
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +2 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +33 -7
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +10 -3
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +4 -24
- package/dist/resources/extensions/gsd/commands-verdict.js +10 -9
- package/dist/resources/extensions/gsd/compat/compat-marker.js +80 -5
- package/dist/resources/extensions/gsd/constants.js +12 -13
- package/dist/resources/extensions/gsd/context-masker.js +97 -21
- package/dist/resources/extensions/gsd/db/engine.js +5 -2
- package/dist/resources/extensions/gsd/docs/preferences-reference.md +22 -0
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +24 -8
- package/dist/resources/extensions/gsd/error-classifier.js +12 -2
- package/dist/resources/extensions/gsd/exec-sandbox.js +1 -0
- package/dist/resources/extensions/gsd/flat-phase-migration.js +12 -0
- package/dist/resources/extensions/gsd/git-service.js +19 -7
- package/dist/resources/extensions/gsd/gsd-db.js +75 -31
- package/dist/resources/extensions/gsd/guided-flow.js +11 -5
- package/dist/resources/extensions/gsd/markdown-renderer.js +5 -21
- package/dist/resources/extensions/gsd/md-importer.js +5 -6
- package/dist/resources/extensions/gsd/model-cost-table.js +2 -0
- package/dist/resources/extensions/gsd/model-router.js +11 -0
- package/dist/resources/extensions/gsd/paths.js +66 -15
- package/dist/resources/extensions/gsd/planning-subagent-policy.js +37 -0
- package/dist/resources/extensions/gsd/planning-subagent-registry.js +25 -0
- package/dist/resources/extensions/gsd/preferences-types.js +5 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +48 -1
- package/dist/resources/extensions/gsd/preferences.js +17 -0
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +5 -5
- package/dist/resources/extensions/gsd/provider-payload-policy.js +16 -5
- package/dist/resources/extensions/gsd/safety/destructive-guard.js +1 -1
- package/dist/resources/extensions/gsd/skill-activation.js +17 -3
- package/dist/resources/extensions/gsd/state-reconciliation/drift/external-markdown-edit.js +98 -2
- package/dist/resources/extensions/gsd/state-reconciliation/drift/external-planning-edit.js +4 -1
- package/dist/resources/extensions/gsd/state-reconciliation/drift/roadmap.js +5 -2
- package/dist/resources/extensions/gsd/stop-notice.js +9 -0
- package/dist/resources/extensions/gsd/templates/PREFERENCES.md +5 -0
- package/dist/resources/extensions/gsd/tools/complete-task.js +12 -5
- package/dist/resources/extensions/gsd/tools/reopen-milestone.js +14 -5
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +15 -5
- package/dist/resources/extensions/gsd/tools/reopen-task.js +16 -10
- package/dist/resources/extensions/gsd/unit-context-composer.js +28 -5
- package/dist/resources/extensions/gsd/workflow-projections.js +2 -3
- package/dist/resources/extensions/gsd/workspace.js +2 -1
- package/dist/resources/extensions/gsd/worktree-state-projection.js +20 -18
- package/dist/resources/extensions/shared/interview-ui.js +43 -6
- 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.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 +8 -8
- package/dist/web/standalone/.next/server/chunks/1991.js +2 -2
- package/dist/web/standalone/.next/server/chunks/5124.js +1 -1
- 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/node_modules/@gsd/native/package.json +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
- package/integrations/hermes/open_gsd_hermes/gsd_client.py +1 -1
- package/integrations/hermes/pyproject.toml +1 -1
- package/package.json +7 -6
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/dist/compaction/compaction.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/compaction/compaction.js +36 -4
- package/packages/gsd-agent-core/dist/compaction/compaction.js.map +1 -1
- package/packages/gsd-agent-core/dist/sdk.d.ts.map +1 -1
- package/packages/gsd-agent-core/dist/sdk.js +1 -0
- package/packages/gsd-agent-core/dist/sdk.js.map +1 -1
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/gsd-cloud/README.md +9 -3
- package/packages/gsd-cloud/dist/background-cli.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/background-cli.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/background-cli.test.js +248 -0
- package/packages/gsd-cloud/dist/background-cli.test.js.map +1 -0
- package/packages/gsd-cloud/dist/cli.d.ts.map +1 -1
- package/packages/gsd-cloud/dist/cli.js +121 -15
- package/packages/gsd-cloud/dist/cli.js.map +1 -1
- package/packages/gsd-cloud/dist/cloud-config.d.ts +1 -1
- package/packages/gsd-cloud/dist/cloud-config.d.ts.map +1 -1
- package/packages/gsd-cloud/dist/cloud-config.js +7 -1
- package/packages/gsd-cloud/dist/cloud-config.js.map +1 -1
- package/packages/gsd-cloud/dist/cloud-runtime.d.ts +1 -3
- package/packages/gsd-cloud/dist/cloud-runtime.d.ts.map +1 -1
- package/packages/gsd-cloud/dist/cloud-runtime.js +9 -7
- package/packages/gsd-cloud/dist/cloud-runtime.js.map +1 -1
- package/packages/gsd-cloud/dist/runtime-process.d.ts +27 -0
- package/packages/gsd-cloud/dist/runtime-process.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/runtime-process.js +272 -0
- package/packages/gsd-cloud/dist/runtime-process.js.map +1 -0
- package/packages/gsd-cloud/dist/runtime-process.test.d.ts +2 -0
- package/packages/gsd-cloud/dist/runtime-process.test.d.ts.map +1 -0
- package/packages/gsd-cloud/dist/runtime-process.test.js +97 -0
- package/packages/gsd-cloud/dist/runtime-process.test.js.map +1 -0
- package/packages/gsd-cloud/package.json +2 -2
- package/packages/mcp-server/README.md +7 -2
- package/packages/mcp-server/dist/cli-runner.d.ts +3 -0
- package/packages/mcp-server/dist/cli-runner.d.ts.map +1 -1
- package/packages/mcp-server/dist/cli-runner.js +119 -33
- package/packages/mcp-server/dist/cli-runner.js.map +1 -1
- package/packages/mcp-server/dist/server.d.ts.map +1 -1
- package/packages/mcp-server/dist/server.js +5 -5
- package/packages/mcp-server/dist/server.js.map +1 -1
- package/packages/mcp-server/dist/workflow-tools.d.ts +4 -3
- package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
- 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/dist/agent.d.ts +5 -1
- package/packages/pi-agent-core/dist/agent.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent.js +2 -0
- package/packages/pi-agent-core/dist/agent.js.map +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +76 -0
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +70 -0
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +6 -1
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.js +1 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.js +103 -0
- package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.js +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/capability-patches.js +1 -1
- package/packages/pi-coding-agent/dist/core/capability-patches.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js +1 -1
- package/packages/pi-coding-agent/dist/core/model-resolver.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +11 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +8 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.d.ts +5 -0
- package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.js +54 -0
- package/packages/pi-coding-agent/dist/core/skills.js.map +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 +2 -2
- package/src/resources/extensions/gsd/artifact-verification.ts +3 -8
- package/src/resources/extensions/gsd/auto/loop.ts +48 -4
- package/src/resources/extensions/gsd/auto/run-unit.ts +20 -1
- package/src/resources/extensions/gsd/auto/unit-phase.ts +41 -5
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +14 -7
- package/src/resources/extensions/gsd/auto-dispatch.ts +1 -0
- package/src/resources/extensions/gsd/auto-prompts.ts +118 -19
- package/src/resources/extensions/gsd/auto-recovery.ts +10 -8
- package/src/resources/extensions/gsd/auto-runtime-state.ts +12 -7
- package/src/resources/extensions/gsd/auto-start.ts +31 -8
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +56 -0
- package/src/resources/extensions/gsd/auto-worktree-cleanup.ts +4 -0
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +10 -3
- package/src/resources/extensions/gsd/auto-worktree-teardown.ts +1 -1
- package/src/resources/extensions/gsd/auto.ts +20 -7
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +1 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +41 -12
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +11 -3
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +7 -29
- package/src/resources/extensions/gsd/commands-verdict.ts +16 -9
- package/src/resources/extensions/gsd/compat/compat-marker.ts +99 -5
- package/src/resources/extensions/gsd/constants.ts +14 -13
- package/src/resources/extensions/gsd/context-masker.ts +98 -2
- package/src/resources/extensions/gsd/db/engine.ts +5 -2
- package/src/resources/extensions/gsd/docs/preferences-reference.md +22 -0
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +30 -7
- package/src/resources/extensions/gsd/error-classifier.ts +13 -2
- package/src/resources/extensions/gsd/exec-sandbox.ts +1 -0
- package/src/resources/extensions/gsd/flat-phase-migration.ts +14 -0
- package/src/resources/extensions/gsd/git-service.ts +19 -6
- package/src/resources/extensions/gsd/gsd-db.ts +85 -33
- package/src/resources/extensions/gsd/guided-flow.ts +11 -4
- package/src/resources/extensions/gsd/markdown-renderer.ts +5 -23
- package/src/resources/extensions/gsd/md-importer.ts +5 -5
- package/src/resources/extensions/gsd/model-cost-table.ts +2 -0
- package/src/resources/extensions/gsd/model-router.ts +12 -0
- package/src/resources/extensions/gsd/paths.ts +73 -15
- package/src/resources/extensions/gsd/planning-subagent-policy.ts +58 -0
- package/src/resources/extensions/gsd/planning-subagent-registry.ts +31 -0
- package/src/resources/extensions/gsd/preferences-types.ts +16 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +62 -0
- package/src/resources/extensions/gsd/preferences.ts +25 -0
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +5 -5
- package/src/resources/extensions/gsd/provider-payload-policy.ts +18 -4
- package/src/resources/extensions/gsd/safety/destructive-guard.ts +1 -1
- package/src/resources/extensions/gsd/skill-activation.ts +17 -3
- package/src/resources/extensions/gsd/state-reconciliation/drift/external-markdown-edit.ts +132 -2
- package/src/resources/extensions/gsd/state-reconciliation/drift/external-planning-edit.ts +4 -1
- package/src/resources/extensions/gsd/state-reconciliation/drift/roadmap.ts +4 -2
- package/src/resources/extensions/gsd/stop-notice.ts +11 -0
- package/src/resources/extensions/gsd/templates/PREFERENCES.md +5 -0
- package/src/resources/extensions/gsd/tests/auto-artifact-paths.test.ts +7 -7
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +264 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/auto-start-index-lock.test.ts +21 -1
- package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +74 -1
- package/src/resources/extensions/gsd/tests/before-provider-context-management.test.ts +55 -0
- package/src/resources/extensions/gsd/tests/commands-verdict.test.ts +10 -0
- package/src/resources/extensions/gsd/tests/compat-marker.test.ts +25 -0
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +76 -1
- package/src/resources/extensions/gsd/tests/context-masker.test.ts +204 -8
- package/src/resources/extensions/gsd/tests/db-engine-cjs-compat.test.ts +12 -0
- package/src/resources/extensions/gsd/tests/db-tools-alias-suppression.test.ts +62 -0
- package/src/resources/extensions/gsd/tests/destructive-confirmation.test.ts +33 -0
- package/src/resources/extensions/gsd/tests/destructive-guard.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +83 -0
- package/src/resources/extensions/gsd/tests/exec-sandbox.test.ts +33 -1
- package/src/resources/extensions/gsd/tests/external-markdown-edit.test.ts +38 -1
- package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +8 -9
- package/src/resources/extensions/gsd/tests/flat-phase-validation-path.test.ts +20 -1
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/guided-discuss-milestone-prompt-rendering.test.ts +22 -0
- package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +13 -0
- package/src/resources/extensions/gsd/tests/integration/paths.test.ts +22 -4
- package/src/resources/extensions/gsd/tests/integration-branch-meta-location.test.ts +62 -0
- package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +5 -5
- package/src/resources/extensions/gsd/tests/model-cost-table.test.ts +9 -1
- package/src/resources/extensions/gsd/tests/model-router.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/parallel-skill-prompt-integration.test.ts +7 -5
- package/src/resources/extensions/gsd/tests/preferences.test.ts +84 -0
- package/src/resources/extensions/gsd/tests/prefs-wizard-coverage.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/prompt-budget-enforcement.test.ts +87 -0
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +121 -1
- package/src/resources/extensions/gsd/tests/reactive-executor.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/register-hooks-loop-guard-auto.test.ts +105 -0
- package/src/resources/extensions/gsd/tests/service-tier.test.ts +4 -0
- package/src/resources/extensions/gsd/tests/single-writer-v3-tool-surface.test.ts +33 -7
- package/src/resources/extensions/gsd/tests/skill-activation.test.ts +20 -16
- package/src/resources/extensions/gsd/tests/state-reconciliation-drift.test.ts +52 -0
- package/src/resources/extensions/gsd/tests/stop-notice.test.ts +21 -1
- package/src/resources/extensions/gsd/tests/system-context-message-routing.test.ts +13 -7
- package/src/resources/extensions/gsd/tests/terminated-transient.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/tool-invocation-error-loop-break.test.ts +19 -0
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/unit-context-composer.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/validate-milestone-prompt-verification-classes.test.ts +7 -0
- package/src/resources/extensions/gsd/tests/workflow-projections.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +60 -0
- package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +34 -0
- package/src/resources/extensions/gsd/tools/complete-task.ts +20 -5
- package/src/resources/extensions/gsd/tools/reopen-milestone.ts +24 -5
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +25 -5
- package/src/resources/extensions/gsd/tools/reopen-task.ts +23 -9
- package/src/resources/extensions/gsd/unit-context-composer.ts +34 -8
- package/src/resources/extensions/gsd/workflow-projections.ts +2 -3
- package/src/resources/extensions/gsd/workspace.ts +2 -1
- package/src/resources/extensions/gsd/worktree-state-projection.ts +21 -24
- package/src/resources/extensions/shared/interview-ui.ts +47 -8
- package/src/resources/extensions/shared/tests/interview-ui-border.test.ts +29 -0
- /package/dist/web/standalone/.next/static/{BE5XoklToI6lZjRzpXih8 → EAhnhYMt5UVOiG9-iPDjc}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{BE5XoklToI6lZjRzpXih8 → EAhnhYMt5UVOiG9-iPDjc}/_ssgManifest.js +0 -0
|
@@ -56,6 +56,10 @@ function buildBlock(
|
|
|
56
56
|
});
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
function expectedSkillRead(base: string, name: string): string {
|
|
60
|
+
return `Read the skill file at \`${join(base, "skills", name, "SKILL.md")}\``;
|
|
61
|
+
}
|
|
62
|
+
|
|
59
63
|
test("buildSkillActivationBlock does not auto-activate skills via broad context heuristic", () => {
|
|
60
64
|
const base = makeTempBase();
|
|
61
65
|
try {
|
|
@@ -92,7 +96,7 @@ test("buildSkillActivationBlock activates skills via prefer_skills when context
|
|
|
92
96
|
prefer_skills: ["react"],
|
|
93
97
|
});
|
|
94
98
|
|
|
95
|
-
assert.
|
|
99
|
+
assert.ok(result.includes(expectedSkillRead(base, "react")));
|
|
96
100
|
assert.doesNotMatch(result, /swiftui/);
|
|
97
101
|
} finally {
|
|
98
102
|
cleanup(base);
|
|
@@ -111,7 +115,7 @@ test("buildSkillActivationBlock includes always_use_skills using read-based skil
|
|
|
111
115
|
|
|
112
116
|
assert.equal(
|
|
113
117
|
result,
|
|
114
|
-
|
|
118
|
+
`<skill_activation>Read the skill file at \`${join(base, "skills", "swift-testing", "SKILL.md")}\`.</skill_activation>`,
|
|
115
119
|
);
|
|
116
120
|
} finally {
|
|
117
121
|
cleanup(base);
|
|
@@ -140,8 +144,8 @@ test("buildSkillActivationBlock includes skill_rules matches and task-plan skill
|
|
|
140
144
|
skill_rules: [{ when: "prisma database schema", use: ["prisma"] }],
|
|
141
145
|
});
|
|
142
146
|
|
|
143
|
-
assert.
|
|
144
|
-
assert.
|
|
147
|
+
assert.ok(result.includes(expectedSkillRead(base, "accessibility")));
|
|
148
|
+
assert.ok(result.includes(expectedSkillRead(base, "prisma")));
|
|
145
149
|
} finally {
|
|
146
150
|
cleanup(base);
|
|
147
151
|
}
|
|
@@ -163,7 +167,7 @@ test("buildSkillActivationBlock matches skill_rules against exact unit type cont
|
|
|
163
167
|
],
|
|
164
168
|
});
|
|
165
169
|
|
|
166
|
-
assert.
|
|
170
|
+
assert.ok(result.includes(expectedSkillRead(base, "complete-slice-policies")));
|
|
167
171
|
assert.doesNotMatch(result, /slice-broad/);
|
|
168
172
|
} finally {
|
|
169
173
|
cleanup(base);
|
|
@@ -267,8 +271,8 @@ test("buildSkillActivationBlock allows valid skill names and rejects invalid one
|
|
|
267
271
|
});
|
|
268
272
|
|
|
269
273
|
assert.match(result, /skill_activation/);
|
|
270
|
-
assert.
|
|
271
|
-
assert.
|
|
274
|
+
assert.ok(result.includes(expectedSkillRead(base, "react")));
|
|
275
|
+
assert.ok(result.includes(expectedSkillRead(base, "good-skill-2")));
|
|
272
276
|
assert.doesNotMatch(result, /bad'name/);
|
|
273
277
|
} finally {
|
|
274
278
|
cleanup(base);
|
|
@@ -292,8 +296,8 @@ test("buildSkillActivationBlock: explicit always_use_skills bypass the unit-type
|
|
|
292
296
|
always_use_skills: ["write-docs", "swiftui"],
|
|
293
297
|
});
|
|
294
298
|
|
|
295
|
-
assert.
|
|
296
|
-
assert.
|
|
299
|
+
assert.ok(result.includes(expectedSkillRead(base, "write-docs")));
|
|
300
|
+
assert.ok(result.includes(expectedSkillRead(base, "swiftui")));
|
|
297
301
|
} finally {
|
|
298
302
|
cleanup(base);
|
|
299
303
|
}
|
|
@@ -310,7 +314,7 @@ test("buildSkillActivationBlock falls through to all skills for unknown unit typ
|
|
|
310
314
|
});
|
|
311
315
|
|
|
312
316
|
// Unknown unit type = wildcard fallback (pre-manifest behavior).
|
|
313
|
-
assert.
|
|
317
|
+
assert.ok(result.includes(expectedSkillRead(base, "swiftui")));
|
|
314
318
|
} finally {
|
|
315
319
|
cleanup(base);
|
|
316
320
|
}
|
|
@@ -327,7 +331,7 @@ test("buildSkillActivationBlock without unitType preserves pre-manifest behavior
|
|
|
327
331
|
always_use_skills: ["swiftui"],
|
|
328
332
|
});
|
|
329
333
|
|
|
330
|
-
assert.
|
|
334
|
+
assert.ok(result.includes(expectedSkillRead(base, "swiftui")));
|
|
331
335
|
} finally {
|
|
332
336
|
cleanup(base);
|
|
333
337
|
}
|
|
@@ -344,12 +348,12 @@ test("milestone prompt builders propagate always_use_skills through buildSkillAc
|
|
|
344
348
|
loadOnlyTestSkills(base);
|
|
345
349
|
|
|
346
350
|
const researchPrompt = await buildResearchMilestonePrompt("M001", "Test", base);
|
|
347
|
-
assert.
|
|
348
|
-
assert.
|
|
351
|
+
assert.ok(researchPrompt.includes(expectedSkillRead(base, "write-docs")));
|
|
352
|
+
assert.ok(researchPrompt.includes(expectedSkillRead(base, "swiftui")));
|
|
349
353
|
|
|
350
354
|
const planPrompt = await buildPlanMilestonePrompt("M001", "Test", base);
|
|
351
|
-
assert.
|
|
352
|
-
assert.
|
|
355
|
+
assert.ok(planPrompt.includes(expectedSkillRead(base, "write-docs")));
|
|
356
|
+
assert.ok(planPrompt.includes(expectedSkillRead(base, "swiftui")));
|
|
353
357
|
} finally {
|
|
354
358
|
cleanup(base);
|
|
355
359
|
}
|
|
@@ -380,7 +384,7 @@ test("complete-slice prompt propagates always_use_skills through buildSkillActiv
|
|
|
380
384
|
|
|
381
385
|
const prompt = await buildCompleteSlicePrompt("M001", "Test", "S01", "Slice", base);
|
|
382
386
|
|
|
383
|
-
assert.
|
|
387
|
+
assert.ok(prompt.includes(expectedSkillRead(base, "write-docs")));
|
|
384
388
|
} finally {
|
|
385
389
|
cleanup(base);
|
|
386
390
|
}
|
|
@@ -1618,6 +1618,58 @@ test("ADR-017 (#5705): in-sync ROADMAP and DB → no roadmap-divergence drift",
|
|
|
1618
1618
|
);
|
|
1619
1619
|
});
|
|
1620
1620
|
|
|
1621
|
+
test("ADR-017 (#1370): roadmap-divergence skips completed milestone sharing active worktree phase", async (t) => {
|
|
1622
|
+
const base = mkdtempSync(join(tmpdir(), "gsd-adr017-roadmap-shared-phase-"));
|
|
1623
|
+
const milestoneDir = join(base, ".gsd", "phases", "06-rlrbot-m006-rlrbot-tool-registry-and-command-sa");
|
|
1624
|
+
const roadmapPath = join(milestoneDir, "06-ROADMAP.md");
|
|
1625
|
+
mkdirSync(milestoneDir, { recursive: true });
|
|
1626
|
+
mkdirSync(join(base, ".gsd", "milestones", "M006"), { recursive: true });
|
|
1627
|
+
mkdirSync(join(base, ".gsd", "milestones", "M006-rlrbot"), { recursive: true });
|
|
1628
|
+
writeFileSync(join(base, ".gsd", "milestones", "M006", "M006-META.json"), "{}\n");
|
|
1629
|
+
writeFileSync(join(base, ".gsd", "milestones", "M006-rlrbot", "M006-rlrbot-META.json"), "{}\n");
|
|
1630
|
+
const activeRoadmap = [
|
|
1631
|
+
"# M006-rlrbot: Active worktree",
|
|
1632
|
+
"",
|
|
1633
|
+
"**Vision:** Keep the active worktree projection authoritative.",
|
|
1634
|
+
"",
|
|
1635
|
+
"## Slices",
|
|
1636
|
+
"",
|
|
1637
|
+
"- [ ] **S01: Shared slice** `risk:medium` `depends:[]`",
|
|
1638
|
+
" > After this: Active work remains pending.",
|
|
1639
|
+
"",
|
|
1640
|
+
].join("\n");
|
|
1641
|
+
writeFileSync(roadmapPath, activeRoadmap);
|
|
1642
|
+
t.after(() => {
|
|
1643
|
+
try { closeDatabase(); } catch { /* noop */ }
|
|
1644
|
+
rmSync(base, { recursive: true, force: true });
|
|
1645
|
+
});
|
|
1646
|
+
|
|
1647
|
+
openDatabase(join(base, ".gsd", "gsd.db"));
|
|
1648
|
+
insertMilestone({ id: "M006", title: "Completed source", status: "complete" });
|
|
1649
|
+
insertSlice({ id: "S01", milestoneId: "M006", title: "Shared slice", status: "complete", risk: "medium", depends: [], demo: "Completed.", sequence: 1 });
|
|
1650
|
+
insertMilestone({
|
|
1651
|
+
id: "M006-rlrbot",
|
|
1652
|
+
title: "Active worktree",
|
|
1653
|
+
status: "active",
|
|
1654
|
+
planning: { vision: "Keep the active worktree projection authoritative." },
|
|
1655
|
+
});
|
|
1656
|
+
insertSlice({ id: "S01", milestoneId: "M006-rlrbot", title: "Shared slice", status: "pending", risk: "medium", depends: [], demo: "Active work remains pending.", sequence: 1 });
|
|
1657
|
+
insertTask({ id: "T01", sliceId: "S01", milestoneId: "M006-rlrbot", title: "Plan active work", status: "pending" });
|
|
1658
|
+
|
|
1659
|
+
const result = await reconcileBeforeDispatch(base, {
|
|
1660
|
+
invalidateStateCache: () => {},
|
|
1661
|
+
deriveState: async () => makeState({ activeMilestone: { id: "M006-rlrbot", title: "Active worktree" } }),
|
|
1662
|
+
});
|
|
1663
|
+
|
|
1664
|
+
assert.equal(result.ok, true);
|
|
1665
|
+
assert.equal(
|
|
1666
|
+
result.repaired.some((d) => d.kind === "roadmap-divergence"),
|
|
1667
|
+
false,
|
|
1668
|
+
"completed milestone sharing the phase dir must not trigger roadmap-divergence repair",
|
|
1669
|
+
);
|
|
1670
|
+
assert.equal(readFileSync(roadmapPath, "utf-8"), activeRoadmap);
|
|
1671
|
+
});
|
|
1672
|
+
|
|
1621
1673
|
// ─── #5706: missing-completion-timestamp drift ──────────────────────────────
|
|
1622
1674
|
|
|
1623
1675
|
test("ADR-017 (#5706): task with SUMMARY but null completed_at → backfilled", async (t) => {
|
|
@@ -7,6 +7,8 @@ import assert from "node:assert/strict";
|
|
|
7
7
|
|
|
8
8
|
import {
|
|
9
9
|
formatStopNoticePrefix,
|
|
10
|
+
formatVerdictRecordedNotice,
|
|
11
|
+
formatVerdictRejectedNotice,
|
|
10
12
|
isBlockedStopReason,
|
|
11
13
|
stopNoticeDisplayReason,
|
|
12
14
|
stopNoticeKind,
|
|
@@ -63,6 +65,17 @@ describe("emitter↔detector round-trip", () => {
|
|
|
63
65
|
assert.ok(isBlockedNoticeMessage(message));
|
|
64
66
|
});
|
|
65
67
|
|
|
68
|
+
test("verdict notices classify as terminal with rejected notices blocked", () => {
|
|
69
|
+
const recorded = formatVerdictRecordedNotice("Milestone M001 verdict: needs-attention -> pass").toLowerCase();
|
|
70
|
+
const rejected = formatVerdictRejectedNotice("No milestone validation found for M001.").toLowerCase();
|
|
71
|
+
|
|
72
|
+
assert.ok(isTerminalNotice(recorded));
|
|
73
|
+
assert.equal(isBlockedNoticeMessage(recorded), false);
|
|
74
|
+
|
|
75
|
+
assert.ok(isTerminalNotice(rejected));
|
|
76
|
+
assert.ok(isBlockedNoticeMessage(rejected));
|
|
77
|
+
});
|
|
78
|
+
|
|
66
79
|
test("un-showable menu notices classify as blocked (#1294)", () => {
|
|
67
80
|
// Emitted verbatim by notifyCommandMenuUnavailable (next-action-ui.ts / command-feedback.ts).
|
|
68
81
|
const menuUnavailable =
|
|
@@ -80,7 +93,14 @@ describe("emitter↔detector round-trip", () => {
|
|
|
80
93
|
});
|
|
81
94
|
|
|
82
95
|
test("terminal prefixes cover the known stop vocabulary", () => {
|
|
83
|
-
for (const message of [
|
|
96
|
+
for (const message of [
|
|
97
|
+
"auto-mode stopped.",
|
|
98
|
+
"auto-mode complete",
|
|
99
|
+
"auto-mode idle",
|
|
100
|
+
"no active milestone",
|
|
101
|
+
"verdict recorded: milestone m001 verdict: needs-attention -> pass",
|
|
102
|
+
"verdict rejected: unexpected argument: x",
|
|
103
|
+
]) {
|
|
84
104
|
assert.ok(TERMINAL_NOTICE_PREFIXES.some((prefix) => message.startsWith(prefix)), message);
|
|
85
105
|
}
|
|
86
106
|
});
|
|
@@ -4,7 +4,13 @@
|
|
|
4
4
|
import { describe, test } from "node:test";
|
|
5
5
|
import assert from "node:assert/strict";
|
|
6
6
|
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
buildContextMessage,
|
|
9
|
+
GSD_CONTEXT_MESSAGE_SENTINEL,
|
|
10
|
+
stripVolatileCodebaseMetadata,
|
|
11
|
+
} from "../bootstrap/system-context.ts";
|
|
12
|
+
|
|
13
|
+
const SENTINEL = `${GSD_CONTEXT_MESSAGE_SENTINEL}\n`;
|
|
8
14
|
|
|
9
15
|
describe("stripVolatileCodebaseMetadata (#847 — KV cache stability)", () => {
|
|
10
16
|
const map = [
|
|
@@ -63,7 +69,7 @@ describe("buildContextMessage (#5019 — memory routing)", () => {
|
|
|
63
69
|
});
|
|
64
70
|
assert.ok(result, "expected a context message");
|
|
65
71
|
assert.equal(result.customType, "gsd-memory");
|
|
66
|
-
assert.equal(result.content,
|
|
72
|
+
assert.equal(result.content, `${SENTINEL}[GSD Context Metadata]\n- Memory supplied: yes\n\n[MEMORY]\nrule one\nrule two`);
|
|
67
73
|
assert.equal(result.display, false);
|
|
68
74
|
});
|
|
69
75
|
|
|
@@ -75,7 +81,7 @@ describe("buildContextMessage (#5019 — memory routing)", () => {
|
|
|
75
81
|
});
|
|
76
82
|
assert.ok(result);
|
|
77
83
|
assert.equal(result.customType, "gsd-guided-context");
|
|
78
|
-
assert.equal(result.content,
|
|
84
|
+
assert.equal(result.content, `${SENTINEL}[GUIDED]\nexecute T01`);
|
|
79
85
|
});
|
|
80
86
|
|
|
81
87
|
test("forensics injection alone emits gsd-forensics", () => {
|
|
@@ -86,7 +92,7 @@ describe("buildContextMessage (#5019 — memory routing)", () => {
|
|
|
86
92
|
});
|
|
87
93
|
assert.ok(result);
|
|
88
94
|
assert.equal(result.customType, "gsd-forensics");
|
|
89
|
-
assert.equal(result.content,
|
|
95
|
+
assert.equal(result.content, `${SENTINEL}[FORENSICS]\ninvestigation context`);
|
|
90
96
|
});
|
|
91
97
|
|
|
92
98
|
test("memory + guided injection: memory prepended, customType is gsd-guided-context", () => {
|
|
@@ -97,7 +103,7 @@ describe("buildContextMessage (#5019 — memory routing)", () => {
|
|
|
97
103
|
});
|
|
98
104
|
assert.ok(result);
|
|
99
105
|
assert.equal(result.customType, "gsd-guided-context");
|
|
100
|
-
assert.equal(result.content, `${markedMemory}\n\n[GUIDED]\nexecute T01`);
|
|
106
|
+
assert.equal(result.content, `${SENTINEL}${markedMemory}\n\n[GUIDED]\nexecute T01`);
|
|
101
107
|
});
|
|
102
108
|
|
|
103
109
|
test("memory + forensics: memory prepended, customType is gsd-forensics", () => {
|
|
@@ -108,7 +114,7 @@ describe("buildContextMessage (#5019 — memory routing)", () => {
|
|
|
108
114
|
});
|
|
109
115
|
assert.ok(result);
|
|
110
116
|
assert.equal(result.customType, "gsd-forensics");
|
|
111
|
-
assert.equal(result.content, `${markedMemory}\n\n[FORENSICS]\ninvestigation context`);
|
|
117
|
+
assert.equal(result.content, `${SENTINEL}${markedMemory}\n\n[FORENSICS]\ninvestigation context`);
|
|
112
118
|
});
|
|
113
119
|
|
|
114
120
|
test("guided takes precedence over forensics when both are somehow present", () => {
|
|
@@ -122,6 +128,6 @@ describe("buildContextMessage (#5019 — memory routing)", () => {
|
|
|
122
128
|
});
|
|
123
129
|
assert.ok(result);
|
|
124
130
|
assert.equal(result.customType, "gsd-guided-context");
|
|
125
|
-
assert.equal(result.content,
|
|
131
|
+
assert.equal(result.content, `${SENTINEL}[GUIDED]`);
|
|
126
132
|
});
|
|
127
133
|
});
|
|
@@ -17,6 +17,20 @@ test("#2309: 'terminated' errors should be classified as transient", () => {
|
|
|
17
17
|
assert.equal("retryAfterMs" in result && result.retryAfterMs, 15_000, "'terminated' should use 15s backoff");
|
|
18
18
|
});
|
|
19
19
|
|
|
20
|
+
test("#1364: provider abort stop reasons should be classified as transient", () => {
|
|
21
|
+
const result = classifyError("Unhandled stop reason: abort");
|
|
22
|
+
assert.equal(isTransient(result), true, "'abort' provider errors should be transient");
|
|
23
|
+
assert.equal(result.kind, "connection", "'abort' matches connection");
|
|
24
|
+
assert.equal("retryAfterMs" in result && result.retryAfterMs, 15_000, "'abort' should use 15s backoff");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("#1364: provider unknown stop reasons should be classified as transient", () => {
|
|
28
|
+
const result = classifyError("Unhandled stop reason: unknown");
|
|
29
|
+
assert.equal(isTransient(result), true, "'unknown' provider stop reasons should be transient");
|
|
30
|
+
assert.equal(result.kind, "connection", "'unknown' stop reasons match connection");
|
|
31
|
+
assert.equal("retryAfterMs" in result && result.retryAfterMs, 15_000, "'unknown' should use 15s backoff");
|
|
32
|
+
});
|
|
33
|
+
|
|
20
34
|
test("#2309: 'connection reset by peer' errors should be classified as transient (network)", () => {
|
|
21
35
|
const result = classifyError("connection reset by peer");
|
|
22
36
|
assert.equal(isTransient(result), true, "'connection reset by peer' should be transient");
|
|
@@ -47,6 +47,7 @@ describe("#2883: tool invocation error tracking on AutoSession", () => {
|
|
|
47
47
|
import {
|
|
48
48
|
isDeterministicPolicyError,
|
|
49
49
|
isPendingUserApprovalGateError,
|
|
50
|
+
isScheduleWakeupContinuationError,
|
|
50
51
|
isToolInvocationError,
|
|
51
52
|
isToolUnavailableError,
|
|
52
53
|
isQueuedUserMessageSkip,
|
|
@@ -118,6 +119,24 @@ describe("#2883: isToolInvocationError classification", () => {
|
|
|
118
119
|
assert.equal(isToolInvocationError("Invalid arguments for tool gsd_slice_complete"), true);
|
|
119
120
|
});
|
|
120
121
|
|
|
122
|
+
test("scopes conditional required-field validation to ScheduleWakeup", () => {
|
|
123
|
+
const error = "`prompt` is required when `stop` is not true";
|
|
124
|
+
|
|
125
|
+
assert.equal(isToolInvocationError(error), false);
|
|
126
|
+
assert.equal(
|
|
127
|
+
isScheduleWakeupContinuationError("ScheduleWakeup", error),
|
|
128
|
+
true,
|
|
129
|
+
);
|
|
130
|
+
assert.equal(isScheduleWakeupContinuationError("read", error), false);
|
|
131
|
+
assert.equal(
|
|
132
|
+
isScheduleWakeupContinuationError(
|
|
133
|
+
"ScheduleWakeup",
|
|
134
|
+
"`evidence` is required when `status` is blocking",
|
|
135
|
+
),
|
|
136
|
+
false,
|
|
137
|
+
);
|
|
138
|
+
});
|
|
139
|
+
|
|
121
140
|
test("detects 'No such tool available' error", () => {
|
|
122
141
|
assert.equal(isToolInvocationError("No such tool available: mcp__gsd-workflow__memory_query"), true);
|
|
123
142
|
});
|
|
@@ -25,8 +25,16 @@ function makeMockPi() {
|
|
|
25
25
|
|
|
26
26
|
console.log('\n── Tool naming: registration count ──');
|
|
27
27
|
|
|
28
|
+
// Aliases are hidden from the model-facing surface by default (plan 035);
|
|
29
|
+
// opt in here so this file can keep exercising alias-registration behavior.
|
|
30
|
+
const previousAdvertiseAliases = process.env.GSD_ADVERTISE_TOOL_ALIASES;
|
|
31
|
+
process.env.GSD_ADVERTISE_TOOL_ALIASES = "1";
|
|
32
|
+
|
|
28
33
|
const pi = makeMockPi();
|
|
29
34
|
registerDbTools(pi);
|
|
35
|
+
|
|
36
|
+
if (previousAdvertiseAliases === undefined) delete process.env.GSD_ADVERTISE_TOOL_ALIASES;
|
|
37
|
+
else process.env.GSD_ADVERTISE_TOOL_ALIASES = previousAdvertiseAliases;
|
|
30
38
|
const toolByName = new Map<string, any>(pi.tools.map((tool: any) => [tool.name, tool]));
|
|
31
39
|
const registeredCanonicalNames = new Set<string>(
|
|
32
40
|
WORKFLOW_TOOL_CONTRACTS
|
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
insertTask,
|
|
47
47
|
saveReworkBrief,
|
|
48
48
|
} from "../gsd-db.ts";
|
|
49
|
+
import { clearGSDPreferencesCache, getProjectGSDPreferencesPath } from "../preferences.ts";
|
|
49
50
|
|
|
50
51
|
// ─── Pure composer tests ──────────────────────────────────────────────────
|
|
51
52
|
|
|
@@ -418,6 +419,44 @@ test("Tool Surface composer: planning-dispatch lists allowed subagents", () => {
|
|
|
418
419
|
assert.match(out, /\*\*planner\*\*/);
|
|
419
420
|
});
|
|
420
421
|
|
|
422
|
+
test("Tool Surface composer: planning_subagents updates plan-milestone dispatch guidance", (t) => {
|
|
423
|
+
const originalGsdHome = process.env.GSD_HOME;
|
|
424
|
+
const tempProject = mkdtempSync(join(tmpdir(), "gsd-tool-surface-planning-subagents-"));
|
|
425
|
+
const tempGsdHome = mkdtempSync(join(tmpdir(), "gsd-tool-surface-planning-subagents-home-"));
|
|
426
|
+
|
|
427
|
+
t.after(() => {
|
|
428
|
+
clearGSDPreferencesCache();
|
|
429
|
+
if (originalGsdHome === undefined) delete process.env.GSD_HOME;
|
|
430
|
+
else process.env.GSD_HOME = originalGsdHome;
|
|
431
|
+
rmSync(tempProject, { recursive: true, force: true });
|
|
432
|
+
rmSync(tempGsdHome, { recursive: true, force: true });
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
process.env.GSD_HOME = tempGsdHome;
|
|
436
|
+
mkdirSync(join(tempProject, ".gsd"), { recursive: true });
|
|
437
|
+
clearGSDPreferencesCache();
|
|
438
|
+
writeFileSync(
|
|
439
|
+
getProjectGSDPreferencesPath(tempProject),
|
|
440
|
+
[
|
|
441
|
+
"---",
|
|
442
|
+
"version: 1",
|
|
443
|
+
"planning_subagents:",
|
|
444
|
+
" plan-milestone:",
|
|
445
|
+
" allowed: [security]",
|
|
446
|
+
"---",
|
|
447
|
+
"",
|
|
448
|
+
].join("\n"),
|
|
449
|
+
"utf-8",
|
|
450
|
+
);
|
|
451
|
+
|
|
452
|
+
const out = composeToolSurfaceInstructions("plan-milestone", {
|
|
453
|
+
renderMode: "standalone",
|
|
454
|
+
basePath: tempProject,
|
|
455
|
+
});
|
|
456
|
+
assert.match(out, /Dispatch subagents only to \*\*security\*\*/);
|
|
457
|
+
assert.doesNotMatch(out, /Do not dispatch subagents/);
|
|
458
|
+
});
|
|
459
|
+
|
|
421
460
|
test("Tool Surface composer: execute-task warns against slice/milestone closeout tools", () => {
|
|
422
461
|
const out = composeToolSurfaceInstructions("execute-task", { renderMode: "nested" });
|
|
423
462
|
assert.match(out, /^Tool surface: /);
|
package/src/resources/extensions/gsd/tests/validate-milestone-prompt-verification-classes.test.ts
CHANGED
|
@@ -22,3 +22,10 @@ test("validate-milestone prompt routes verification class analysis into verifica
|
|
|
22
22
|
assert.match(prompt, /If Reviewer C omitted a planned class, reconstruct the missing row/i);
|
|
23
23
|
assert.match(prompt, /Do not call `gsd_validate_milestone` with a partial `verificationClasses` table/i);
|
|
24
24
|
});
|
|
25
|
+
|
|
26
|
+
test("validate-milestone prompt forbids reading phase directories as files", () => {
|
|
27
|
+
assert.match(prompt, /find \.gsd -type f/i);
|
|
28
|
+
assert.match(prompt, /\.gsd\/phases\/<NN>-<slug>\/` is a directory, not an artifact/i);
|
|
29
|
+
assert.match(prompt, /never pass a phase, slice, `tasks\/`, or `slices\/` directory/i);
|
|
30
|
+
assert.doesNotMatch(prompt, /Read a full SUMMARY under `\.gsd\/milestones\/\{\{milestoneId\}\}\/slices\/`/i);
|
|
31
|
+
});
|
|
@@ -352,7 +352,7 @@ test('workflow-projections: regenerateIfMissing SUMMARY is idempotent for flat-p
|
|
|
352
352
|
keyDecisions: ['Use centralized task summary paths.'],
|
|
353
353
|
});
|
|
354
354
|
|
|
355
|
-
const summaryPath = join(phaseDir, 'T01-SUMMARY.md');
|
|
355
|
+
const summaryPath = join(phaseDir, 'S01-T01-SUMMARY.md');
|
|
356
356
|
assert.ok(!existsSync(summaryPath), 'precondition: flat-phase task summary absent');
|
|
357
357
|
|
|
358
358
|
const first = await regenerateIfMissing(base, 'M001', 'S01', 'SUMMARY');
|
|
@@ -399,7 +399,7 @@ test('workflow-projections: flat-phase SUMMARY regeneration ignores stale nested
|
|
|
399
399
|
status: 'complete',
|
|
400
400
|
});
|
|
401
401
|
|
|
402
|
-
const rootSummaryPath = join(phaseDir, 'T01-SUMMARY.md');
|
|
402
|
+
const rootSummaryPath = join(phaseDir, 'S01-T01-SUMMARY.md');
|
|
403
403
|
assert.ok(!existsSync(rootSummaryPath), 'precondition: canonical flat-phase task summary absent');
|
|
404
404
|
|
|
405
405
|
const regenerated = await regenerateIfMissing(base, 'M001', 'S01', 'SUMMARY');
|
|
@@ -440,8 +440,8 @@ test('workflow-projections: renderSummaryProjection uses milestone title when cr
|
|
|
440
440
|
status: 'complete',
|
|
441
441
|
});
|
|
442
442
|
|
|
443
|
-
const titleSummaryPath = join(base, '.gsd', 'phases', '01-milestone', 'T01-SUMMARY.md');
|
|
444
|
-
const idSummaryPath = join(base, '.gsd', 'phases', '01-m001', 'T01-SUMMARY.md');
|
|
443
|
+
const titleSummaryPath = join(base, '.gsd', 'phases', '01-milestone', 'S01-T01-SUMMARY.md');
|
|
444
|
+
const idSummaryPath = join(base, '.gsd', 'phases', '01-m001', 'S01-T01-SUMMARY.md');
|
|
445
445
|
|
|
446
446
|
renderSummaryProjection(base, 'M001', 'S01', 'T01');
|
|
447
447
|
|
|
@@ -117,8 +117,8 @@ test("projectRootToWorktree projects flat-phase discuss artifacts before dispatc
|
|
|
117
117
|
new WorktreeStateProjection().projectRootToWorktree(scope);
|
|
118
118
|
|
|
119
119
|
assert.ok(
|
|
120
|
-
existsSync(join(worktree, ".gsd", "milestones"
|
|
121
|
-
"
|
|
120
|
+
!existsSync(join(worktree, ".gsd", "milestones")),
|
|
121
|
+
"stale metadata-only legacy milestones/ scaffold is not projected into the worktree",
|
|
122
122
|
);
|
|
123
123
|
assert.ok(
|
|
124
124
|
existsSync(join(worktree, ".gsd", "phases", "01-foundation", "01-CONTEXT.md")),
|
|
@@ -246,6 +246,66 @@ describe('worktree-sync-milestones', async () => {
|
|
|
246
246
|
}
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
+
// ─── 7b. flat-phase sync skips empty legacy milestones root ───────────
|
|
250
|
+
console.log('\n=== 7b. flat-phase sync skips empty legacy milestones root ===');
|
|
251
|
+
{
|
|
252
|
+
const mainBase = mkdtempSync(join(tmpdir(), 'gsd-wt-sync-flat-main-'));
|
|
253
|
+
const wtBase = mkdtempSync(join(tmpdir(), 'gsd-wt-sync-flat-wt-'));
|
|
254
|
+
|
|
255
|
+
try {
|
|
256
|
+
const phaseDir = join(mainBase, '.gsd', 'phases', '01-foundation');
|
|
257
|
+
mkdirSync(phaseDir, { recursive: true });
|
|
258
|
+
mkdirSync(join(mainBase, '.gsd', 'milestones'), { recursive: true });
|
|
259
|
+
mkdirSync(join(wtBase, '.gsd'), { recursive: true });
|
|
260
|
+
writeFileSync(join(phaseDir, '01-CONTEXT.md'), '# Foundation\n');
|
|
261
|
+
|
|
262
|
+
syncGsdStateToWorktree(mainBase, wtBase);
|
|
263
|
+
|
|
264
|
+
assert.ok(
|
|
265
|
+
existsSync(join(wtBase, '.gsd', 'phases', '01-foundation', '01-CONTEXT.md')),
|
|
266
|
+
'flat-phase artifact is synced to worktree',
|
|
267
|
+
);
|
|
268
|
+
assert.ok(
|
|
269
|
+
!existsSync(join(wtBase, '.gsd', 'milestones')),
|
|
270
|
+
'empty legacy milestones/ root is not recreated in worktree',
|
|
271
|
+
);
|
|
272
|
+
} finally {
|
|
273
|
+
rmSync(mainBase, { recursive: true, force: true });
|
|
274
|
+
rmSync(wtBase, { recursive: true, force: true });
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// ─── 7c. flat-phase sync skips metadata-only legacy milestone dirs ────
|
|
279
|
+
console.log('\n=== 7c. flat-phase sync skips metadata-only legacy milestone dirs ===');
|
|
280
|
+
{
|
|
281
|
+
const mainBase = mkdtempSync(join(tmpdir(), 'gsd-wt-sync-flat-meta-main-'));
|
|
282
|
+
const wtBase = mkdtempSync(join(tmpdir(), 'gsd-wt-sync-flat-meta-wt-'));
|
|
283
|
+
|
|
284
|
+
try {
|
|
285
|
+
const phaseDir = join(mainBase, '.gsd', 'phases', '01-foundation');
|
|
286
|
+
const metaDir = join(mainBase, '.gsd', 'milestones', 'M001');
|
|
287
|
+
mkdirSync(phaseDir, { recursive: true });
|
|
288
|
+
mkdirSync(metaDir, { recursive: true });
|
|
289
|
+
mkdirSync(join(wtBase, '.gsd'), { recursive: true });
|
|
290
|
+
writeFileSync(join(phaseDir, '01-CONTEXT.md'), '# Foundation\n');
|
|
291
|
+
writeFileSync(join(metaDir, 'M001-META.json'), '{"integrationBranch":"main"}\n');
|
|
292
|
+
|
|
293
|
+
syncGsdStateToWorktree(mainBase, wtBase);
|
|
294
|
+
|
|
295
|
+
assert.ok(
|
|
296
|
+
existsSync(join(wtBase, '.gsd', 'phases', '01-foundation', '01-CONTEXT.md')),
|
|
297
|
+
'flat-phase artifact is synced to worktree',
|
|
298
|
+
);
|
|
299
|
+
assert.ok(
|
|
300
|
+
!existsSync(join(wtBase, '.gsd', 'milestones')),
|
|
301
|
+
'metadata-only legacy milestones/ scaffold is not recreated in worktree',
|
|
302
|
+
);
|
|
303
|
+
} finally {
|
|
304
|
+
rmSync(mainBase, { recursive: true, force: true });
|
|
305
|
+
rmSync(wtBase, { recursive: true, force: true });
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
249
309
|
// ─── 8. syncWorktreeStateBack does not copy task projections ───────────
|
|
250
310
|
console.log('\n=== 8. syncWorktreeStateBack leaves task projections in worktree ===');
|
|
251
311
|
{
|
|
@@ -13,6 +13,7 @@ import { GSD_PHASE_SCOPE_DISPLAY_REASON, GSD_SECTION_CLOSE_GATE_DISPLAY_REASON }
|
|
|
13
13
|
import { ALLOWED_PLANNING_DISPATCH_AGENTS, shouldBlockPlanningUnit } from '../bootstrap/write-gate.ts';
|
|
14
14
|
import { extractSubagentAgentClasses } from '../bootstrap/subagent-input.ts';
|
|
15
15
|
import { isDeterministicPolicyError } from '../auto-tool-tracking.ts';
|
|
16
|
+
import { applyPlanningSubagentPreferences } from '../planning-subagent-policy.ts';
|
|
16
17
|
import type { ToolsPolicy } from '../unit-context-manifest.ts';
|
|
17
18
|
|
|
18
19
|
const BASE = join('/tmp', 'fake-project');
|
|
@@ -335,6 +336,39 @@ test('planning-dispatch: still allows writes inside .gsd/', () => {
|
|
|
335
336
|
assert.strictEqual(r.block, false);
|
|
336
337
|
});
|
|
337
338
|
|
|
339
|
+
test('planning_subagents: upgrades plan-milestone to controlled read-only dispatch', () => {
|
|
340
|
+
const policy = applyPlanningSubagentPreferences('plan-milestone', PLANNING, {
|
|
341
|
+
planning_subagents: {
|
|
342
|
+
'plan-milestone': { allowed: ['security'] },
|
|
343
|
+
},
|
|
344
|
+
});
|
|
345
|
+
const allowed = shouldBlockPlanningUnit('subagent', '', BASE, 'plan-milestone', policy, ['security']);
|
|
346
|
+
assert.strictEqual(allowed.block, false);
|
|
347
|
+
|
|
348
|
+
const blocked = shouldBlockPlanningUnit('subagent', '', BASE, 'plan-milestone', policy, ['reviewer']);
|
|
349
|
+
assert.strictEqual(blocked.block, true);
|
|
350
|
+
assert.match(blocked.reason!, /planning_subagents/);
|
|
351
|
+
assert.match(blocked.reason!, /permitted agents for this unit: security/);
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
test('planning_subagents: widens plan-slice manifest allowlist without allowing unsafe agents', () => {
|
|
355
|
+
const policy = applyPlanningSubagentPreferences('plan-slice', {
|
|
356
|
+
mode: 'planning-dispatch',
|
|
357
|
+
allowedSubagents: ['scout', 'planner'],
|
|
358
|
+
}, {
|
|
359
|
+
planning_subagents: {
|
|
360
|
+
'plan-slice': { allowed: ['security'] },
|
|
361
|
+
},
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
const added = shouldBlockPlanningUnit('subagent', '', BASE, 'plan-slice', policy, ['security']);
|
|
365
|
+
assert.strictEqual(added.block, false);
|
|
366
|
+
|
|
367
|
+
const unsafe = shouldBlockPlanningUnit('subagent', '', BASE, 'plan-slice', policy, ['worker']);
|
|
368
|
+
assert.strictEqual(unsafe.block, true);
|
|
369
|
+
assert.match(unsafe.reason!, /read-only specialists/);
|
|
370
|
+
});
|
|
371
|
+
|
|
338
372
|
// ─── planning mode: pass-through tools ────────────────────────────────────
|
|
339
373
|
|
|
340
374
|
test('planning-unit: allows read tool', () => {
|
|
@@ -36,7 +36,15 @@ import {
|
|
|
36
36
|
} from "../gsd-db.js";
|
|
37
37
|
import { getWorkflowDatabasePath, ensureWorkflowDbAtPath } from "../db-workspace.js";
|
|
38
38
|
import { getGatesForTurn } from "../gate-registry.js";
|
|
39
|
-
import {
|
|
39
|
+
import {
|
|
40
|
+
buildFlatTaskFileName,
|
|
41
|
+
buildTaskFileName,
|
|
42
|
+
gsdProjectionRoot,
|
|
43
|
+
clearPathCache,
|
|
44
|
+
legacyMilestonesDir,
|
|
45
|
+
resolveMilestonePath,
|
|
46
|
+
resolveSlicePath,
|
|
47
|
+
} from "../paths.js";
|
|
40
48
|
import { resolveCanonicalMilestoneRoot } from "../worktree-manager.js";
|
|
41
49
|
import { checkOwnership, taskUnitKey } from "../unit-ownership.js";
|
|
42
50
|
import { saveFile, clearParseCache } from "../files.js";
|
|
@@ -89,13 +97,20 @@ function taskSummaryPath(
|
|
|
89
97
|
// a legacy layout and breaks all subsequent path resolution for the session.
|
|
90
98
|
const slicePath = resolveSlicePath(basePath, milestoneId, sliceId);
|
|
91
99
|
const phaseDir = resolveMilestonePath(basePath, milestoneId);
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
100
|
+
const legacyBase = legacyMilestonesDir(basePath);
|
|
101
|
+
const isLegacy = phaseDir
|
|
102
|
+
? phaseDir.startsWith(legacyBase + "/") || phaseDir.startsWith(legacyBase + "\\")
|
|
103
|
+
: false;
|
|
104
|
+
if (isLegacy && phaseDir) {
|
|
105
|
+
// Legacy layout: the slice has its own slices/SID/ subdir → tasks/ subdir.
|
|
106
|
+
const legacySlicePath = slicePath && slicePath !== phaseDir
|
|
107
|
+
? slicePath
|
|
108
|
+
: join(phaseDir, "slices", sliceId);
|
|
109
|
+
return join(legacySlicePath, "tasks", buildTaskFileName(taskId, "SUMMARY"));
|
|
95
110
|
}
|
|
96
111
|
if (phaseDir) {
|
|
97
112
|
// Flat-phase: task summaries go in the phase dir (no tasks/ subdir)
|
|
98
|
-
return join(phaseDir,
|
|
113
|
+
return join(phaseDir, buildFlatTaskFileName(sliceId, taskId, "SUMMARY"));
|
|
99
114
|
}
|
|
100
115
|
// Fallback: legacy hardcoded path (milestone/slice dir not on disk yet)
|
|
101
116
|
return join(
|