gsd-pi 2.49.0-dev.de3d9f6 → 2.50.0-dev.9476db8
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/headless-ui.js +12 -2
- package/dist/headless.js +29 -13
- package/dist/resources/extensions/gsd/auto/infra-errors.js +1 -0
- package/dist/resources/extensions/gsd/auto/phases.js +11 -11
- package/dist/resources/extensions/gsd/auto/resolve.js +2 -2
- package/dist/resources/extensions/gsd/auto/run-unit.js +2 -2
- package/dist/resources/extensions/gsd/auto/session.js +4 -0
- package/dist/resources/extensions/gsd/auto-artifact-paths.js +8 -10
- package/dist/resources/extensions/gsd/auto-dashboard.js +6 -3
- package/dist/resources/extensions/gsd/auto-dispatch.js +33 -21
- package/dist/resources/extensions/gsd/auto-post-unit.js +17 -24
- package/dist/resources/extensions/gsd/auto-prompts.js +102 -21
- package/dist/resources/extensions/gsd/auto-recovery.js +62 -184
- package/dist/resources/extensions/gsd/auto-start.js +4 -31
- package/dist/resources/extensions/gsd/auto-timers.js +2 -2
- package/dist/resources/extensions/gsd/auto-verification.js +4 -7
- package/dist/resources/extensions/gsd/auto-worktree.js +257 -113
- package/dist/resources/extensions/gsd/auto.js +7 -5
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +89 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +8 -1
- package/dist/resources/extensions/gsd/branch-patterns.js +13 -0
- package/dist/resources/extensions/gsd/doctor-checks.js +5 -1234
- package/dist/resources/extensions/gsd/doctor-engine-checks.js +168 -0
- package/dist/resources/extensions/gsd/doctor-environment.js +28 -7
- package/dist/resources/extensions/gsd/doctor-git-checks.js +405 -0
- package/dist/resources/extensions/gsd/doctor-global-checks.js +74 -0
- package/dist/resources/extensions/gsd/doctor-runtime-checks.js +600 -0
- package/dist/resources/extensions/gsd/doctor.js +9 -1
- package/dist/resources/extensions/gsd/extension-manifest.json +1 -1
- package/dist/resources/extensions/gsd/git-service.js +9 -10
- package/dist/resources/extensions/gsd/gsd-db.js +124 -1
- package/dist/resources/extensions/gsd/guided-flow-queue.js +10 -11
- package/dist/resources/extensions/gsd/markdown-renderer.js +33 -5
- package/dist/resources/extensions/gsd/preferences-types.js +2 -1
- package/dist/resources/extensions/gsd/preferences-validation.js +39 -0
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +27 -8
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +9 -8
- package/dist/resources/extensions/gsd/prompts/execute-task.md +16 -13
- package/dist/resources/extensions/gsd/prompts/forensics.md +12 -5
- package/dist/resources/extensions/gsd/prompts/gate-evaluate.md +32 -0
- package/dist/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +8 -3
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +3 -0
- package/dist/resources/extensions/gsd/prompts/replan-slice.md +1 -1
- package/dist/resources/extensions/gsd/repo-identity.js +29 -0
- package/dist/resources/extensions/gsd/roadmap-slices.js +2 -2
- package/dist/resources/extensions/gsd/session-forensics.js +6 -11
- package/dist/resources/extensions/gsd/session-lock.js +67 -56
- package/dist/resources/extensions/gsd/state.js +34 -7
- package/dist/resources/extensions/gsd/templates/milestone-summary.md +8 -0
- package/dist/resources/extensions/gsd/templates/plan.md +16 -0
- package/dist/resources/extensions/gsd/templates/roadmap.md +13 -0
- package/dist/resources/extensions/gsd/templates/slice-summary.md +9 -0
- package/dist/resources/extensions/gsd/templates/task-plan.md +24 -0
- package/dist/resources/extensions/gsd/tools/plan-slice.js +14 -1
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +3 -3
- package/dist/resources/extensions/gsd/verdict-parser.js +84 -0
- package/dist/resources/extensions/gsd/worktree-resolver.js +24 -0
- package/dist/resources/extensions/gsd/worktree.js +3 -2
- package/dist/resources/extensions/remote-questions/config.js +3 -5
- package/dist/resources/extensions/search-the-web/native-search.js +8 -3
- package/dist/resources/extensions/search-the-web/tool-search.js +19 -2
- package/dist/resources/skills/github-workflows/references/gh/SKILL.md +22 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
- package/dist/web/standalone/.next/build-manifest.json +3 -3
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/react-loadable-manifest.json +1 -1
- package/dist/web/standalone/.next/required-server-files.json +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- 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 +15 -15
- package/dist/web/standalone/.next/server/chunks/229.js +2 -2
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/.next/static/chunks/4024.7c75ac378de0f2b5.js +9 -0
- package/dist/web/standalone/.next/static/chunks/{webpack-0a4cd455ec4197d2.js → webpack-2473ce2c3879fff4.js} +1 -1
- package/dist/web/standalone/server.js +1 -1
- package/package.json +1 -1
- package/packages/pi-agent-core/dist/agent-loop.d.ts.map +1 -1
- package/packages/pi-agent-core/dist/agent-loop.js +4 -1
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/src/agent-loop.ts +4 -1
- package/packages/pi-ai/dist/providers/openai-codex-responses.js +39 -10
- package/packages/pi-ai/dist/providers/openai-codex-responses.js.map +1 -1
- package/packages/pi-ai/src/providers/openai-codex-responses.ts +39 -8
- package/packages/pi-coding-agent/dist/core/blob-store.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/blob-store.js +8 -3
- package/packages/pi-coding-agent/dist/core/blob-store.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/discovery-cache.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/discovery-cache.js +9 -2
- package/packages/pi-coding-agent/dist/core/discovery-cache.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js +1 -1
- package/packages/pi-coding-agent/dist/core/retry-handler.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +7 -32
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/jsonl.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/jsonl.js +5 -0
- package/packages/pi-coding-agent/dist/modes/rpc/jsonl.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js +0 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-client.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js +1 -1
- package/packages/pi-coding-agent/dist/modes/rpc/rpc-mode.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/blob-store.ts +6 -3
- package/packages/pi-coding-agent/src/core/discovery-cache.ts +9 -2
- package/packages/pi-coding-agent/src/core/retry-handler.ts +1 -1
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +7 -32
- package/packages/pi-coding-agent/src/modes/rpc/jsonl.ts +6 -0
- package/packages/pi-coding-agent/src/modes/rpc/rpc-client.ts +0 -2
- package/packages/pi-coding-agent/src/modes/rpc/rpc-mode.ts +2 -2
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/auto/infra-errors.ts +1 -0
- package/src/resources/extensions/gsd/auto/phases.ts +10 -11
- package/src/resources/extensions/gsd/auto/resolve.ts +3 -3
- package/src/resources/extensions/gsd/auto/run-unit.ts +2 -2
- package/src/resources/extensions/gsd/auto/session.ts +5 -0
- package/src/resources/extensions/gsd/auto/types.ts +13 -0
- package/src/resources/extensions/gsd/auto-artifact-paths.ts +19 -21
- package/src/resources/extensions/gsd/auto-dashboard.ts +5 -2
- package/src/resources/extensions/gsd/auto-dispatch.ts +39 -21
- package/src/resources/extensions/gsd/auto-loop.ts +1 -1
- package/src/resources/extensions/gsd/auto-post-unit.ts +18 -28
- package/src/resources/extensions/gsd/auto-prompts.ts +113 -19
- package/src/resources/extensions/gsd/auto-recovery.ts +65 -199
- package/src/resources/extensions/gsd/auto-start.ts +7 -27
- package/src/resources/extensions/gsd/auto-timers.ts +2 -2
- package/src/resources/extensions/gsd/auto-verification.ts +4 -7
- package/src/resources/extensions/gsd/auto-worktree.ts +305 -108
- package/src/resources/extensions/gsd/auto.ts +11 -10
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +93 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +8 -0
- package/src/resources/extensions/gsd/branch-patterns.ts +16 -0
- package/src/resources/extensions/gsd/doctor-checks.ts +5 -1291
- package/src/resources/extensions/gsd/doctor-engine-checks.ts +182 -0
- package/src/resources/extensions/gsd/doctor-environment.ts +30 -7
- package/src/resources/extensions/gsd/doctor-git-checks.ts +415 -0
- package/src/resources/extensions/gsd/doctor-global-checks.ts +84 -0
- package/src/resources/extensions/gsd/doctor-runtime-checks.ts +626 -0
- package/src/resources/extensions/gsd/doctor.ts +9 -1
- package/src/resources/extensions/gsd/extension-manifest.json +1 -1
- package/src/resources/extensions/gsd/git-service.ts +7 -15
- package/src/resources/extensions/gsd/gsd-db.ts +150 -2
- package/src/resources/extensions/gsd/guided-flow-queue.ts +11 -12
- package/src/resources/extensions/gsd/markdown-renderer.ts +37 -4
- package/src/resources/extensions/gsd/preferences-types.ts +5 -1
- package/src/resources/extensions/gsd/preferences-validation.ts +37 -0
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +27 -8
- package/src/resources/extensions/gsd/prompts/complete-slice.md +9 -8
- package/src/resources/extensions/gsd/prompts/execute-task.md +16 -13
- package/src/resources/extensions/gsd/prompts/forensics.md +12 -5
- package/src/resources/extensions/gsd/prompts/gate-evaluate.md +32 -0
- package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +8 -3
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +3 -0
- package/src/resources/extensions/gsd/prompts/replan-slice.md +1 -1
- package/src/resources/extensions/gsd/repo-identity.ts +28 -0
- package/src/resources/extensions/gsd/roadmap-slices.ts +2 -2
- package/src/resources/extensions/gsd/session-forensics.ts +6 -11
- package/src/resources/extensions/gsd/session-lock.ts +92 -64
- package/src/resources/extensions/gsd/state.ts +38 -5
- package/src/resources/extensions/gsd/templates/milestone-summary.md +8 -0
- package/src/resources/extensions/gsd/templates/plan.md +16 -0
- package/src/resources/extensions/gsd/templates/roadmap.md +13 -0
- package/src/resources/extensions/gsd/templates/slice-summary.md +9 -0
- package/src/resources/extensions/gsd/templates/task-plan.md +24 -0
- package/src/resources/extensions/gsd/tests/agent-end-retry.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +1 -81
- package/src/resources/extensions/gsd/tests/complete-slice.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/complete-task.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +9 -12
- package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +115 -1
- package/src/resources/extensions/gsd/tests/doctor-fixlevel.test.ts +65 -1
- package/src/resources/extensions/gsd/tests/doctor-git.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/gate-dispatch.test.ts +189 -0
- package/src/resources/extensions/gsd/tests/gate-storage.test.ts +156 -0
- package/src/resources/extensions/gsd/tests/git-service.test.ts +49 -0
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/infra-error.test.ts +12 -2
- package/src/resources/extensions/gsd/tests/journal-integration.test.ts +39 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/quality-gates.test.ts +347 -0
- package/src/resources/extensions/gsd/tests/queue-completed-milestone-perf.test.ts +155 -0
- package/src/resources/extensions/gsd/tests/replan-slice.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/repo-identity-worktree.test.ts +32 -0
- package/src/resources/extensions/gsd/tests/roadmap-slices.test.ts +26 -0
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +20 -16
- package/src/resources/extensions/gsd/tests/session-lock-transient-read.test.ts +223 -0
- package/src/resources/extensions/gsd/tests/skill-activation.test.ts +44 -4
- package/src/resources/extensions/gsd/tests/tool-naming.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/verification-gate.test.ts +0 -16
- package/src/resources/extensions/gsd/tests/worktree-resolver.test.ts +67 -0
- package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/worktree-sync-overwrite-loop.test.ts +204 -0
- package/src/resources/extensions/gsd/tools/plan-slice.ts +16 -0
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +3 -3
- package/src/resources/extensions/gsd/types.ts +30 -0
- package/src/resources/extensions/gsd/verdict-parser.ts +95 -0
- package/src/resources/extensions/gsd/verification-gate.ts +0 -2
- package/src/resources/extensions/gsd/worktree-resolver.ts +31 -0
- package/src/resources/extensions/gsd/worktree.ts +3 -2
- package/src/resources/extensions/remote-questions/config.ts +3 -5
- package/src/resources/extensions/search-the-web/native-search.ts +8 -3
- package/src/resources/extensions/search-the-web/tool-search.ts +22 -2
- package/src/resources/skills/github-workflows/references/gh/SKILL.md +22 -1
- package/dist/resources/extensions/gsd/auto-worktree-sync.js +0 -191
- package/dist/resources/extensions/gsd/resource-version.js +0 -97
- package/dist/web/standalone/.next/static/chunks/4024.11ca5c01938e5948.js +0 -9
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +0 -234
- package/src/resources/extensions/gsd/resource-version.ts +0 -101
- /package/dist/web/standalone/.next/static/{ceckLbAMjhzHaQ3RPtJnT → MkE9kzqUGny3-cSE0GNnm}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{ceckLbAMjhzHaQ3RPtJnT → MkE9kzqUGny3-cSE0GNnm}/_ssgManifest.js +0 -0
|
@@ -16,7 +16,7 @@ GSD extension source code is at: `{{gsdSourceDir}}`
|
|
|
16
16
|
|
|
17
17
|
| Domain | Files |
|
|
18
18
|
|--------|-------|
|
|
19
|
-
| **Auto-mode engine** | `auto.ts` `auto-loop.ts` `auto-dispatch.ts` `auto-start.ts` `auto-supervisor.ts` `auto-timers.ts` `auto-timeout-recovery.ts` `auto-unit-closeout.ts` `auto-post-unit.ts` `auto-verification.ts` `auto-recovery.ts` `auto-worktree.ts` `auto-
|
|
19
|
+
| **Auto-mode engine** | `auto.ts` `auto-loop.ts` `auto-dispatch.ts` `auto-start.ts` `auto-supervisor.ts` `auto-timers.ts` `auto-timeout-recovery.ts` `auto-unit-closeout.ts` `auto-post-unit.ts` `auto-verification.ts` `auto-recovery.ts` `auto-worktree.ts` `auto-model-selection.ts` `auto-budget.ts` `dispatch-guard.ts` |
|
|
20
20
|
| **State & persistence** | `state.ts` `types.ts` `files.ts` `paths.ts` `json-persistence.ts` `atomic-write.ts` |
|
|
21
21
|
| **Forensics & recovery** | `forensics.ts` `session-forensics.ts` `crash-recovery.ts` `session-lock.ts` |
|
|
22
22
|
| **Metrics & telemetry** | `metrics.ts` `skill-telemetry.ts` `token-counter.ts` |
|
|
@@ -46,7 +46,7 @@ GSD extension source code is at: `{{gsdSourceDir}}`
|
|
|
46
46
|
├── milestones/{ID}/ — milestone artifacts
|
|
47
47
|
│ ├── {ID}-ROADMAP.md, {ID}-RESEARCH.md, {ID}-CONTEXT.md, {ID}-SUMMARY.md
|
|
48
48
|
│ └── slices/{SID}/ — slice artifacts
|
|
49
|
-
│ ├── {SID}-PLAN.md, {SID}-RESEARCH.md, {SID}-UAT
|
|
49
|
+
│ ├── {SID}-PLAN.md, {SID}-RESEARCH.md, {SID}-UAT.md, {SID}-SUMMARY.md
|
|
50
50
|
│ └── tasks/{TID}-PLAN.md, {TID}-SUMMARY.md
|
|
51
51
|
└── worktrees/{milestoneId}/ — per-milestone worktree with replicated .gsd/
|
|
52
52
|
```
|
|
@@ -142,9 +142,10 @@ Then **offer GitHub issue creation**: "Would you like me to create a GitHub issu
|
|
|
142
142
|
If yes, create using the `bash` tool:
|
|
143
143
|
|
|
144
144
|
```bash
|
|
145
|
-
|
|
145
|
+
# Step 1: Create issue (use labels for metadata, NOT for classification — type is set via GraphQL)
|
|
146
|
+
ISSUE_URL=$(gh issue create --repo gsd-build/gsd-2 \
|
|
146
147
|
--title "..." \
|
|
147
|
-
--label "
|
|
148
|
+
--label "auto-generated" \
|
|
148
149
|
--body "$(cat <<'EOF'
|
|
149
150
|
## Problem
|
|
150
151
|
[1-2 sentence summary]
|
|
@@ -169,7 +170,13 @@ gh issue create --repo gsd-build/gsd-2 \
|
|
|
169
170
|
---
|
|
170
171
|
*Auto-generated by `/gsd forensics`*
|
|
171
172
|
EOF
|
|
172
|
-
)"
|
|
173
|
+
)")
|
|
174
|
+
|
|
175
|
+
# Step 2: Set issue type via GraphQL (gh issue create has no --type flag)
|
|
176
|
+
ISSUE_NUM=$(echo "$ISSUE_URL" | grep -oE '[0-9]+$')
|
|
177
|
+
ISSUE_ID=$(gh api graphql -f query='{ repository(owner:"gsd-build",name:"gsd-2") { issue(number:'"$ISSUE_NUM"') { id } } }' --jq '.data.repository.issue.id')
|
|
178
|
+
TYPE_ID=$(gh api graphql -f query='{ repository(owner:"gsd-build",name:"gsd-2") { issueTypes(first:20) { nodes { id name } } } }' --jq '.data.repository.issueTypes.nodes[] | select(.name=="Bug") | .id')
|
|
179
|
+
gh api graphql -f query='mutation { updateIssue(input:{id:"'"$ISSUE_ID"'",issueTypeId:"'"$TYPE_ID"'"}) { issue { number } } }'
|
|
173
180
|
```
|
|
174
181
|
|
|
175
182
|
### Redaction Rules (CRITICAL)
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Quality Gate Evaluation — Parallel Dispatch
|
|
2
|
+
|
|
3
|
+
**Working directory:** `{{workingDirectory}}`
|
|
4
|
+
**Milestone:** {{milestoneId}} — {{milestoneTitle}}
|
|
5
|
+
**Slice:** {{sliceId}} — {{sliceTitle}}
|
|
6
|
+
|
|
7
|
+
## Mission
|
|
8
|
+
|
|
9
|
+
You are evaluating **quality gates in parallel** for this slice. Each gate is an independent question that must be answered before task execution begins. Use the `subagent` tool to dispatch all gate evaluations simultaneously.
|
|
10
|
+
|
|
11
|
+
## Slice Plan Context
|
|
12
|
+
|
|
13
|
+
{{slicePlanContent}}
|
|
14
|
+
|
|
15
|
+
## Gates to Evaluate
|
|
16
|
+
|
|
17
|
+
{{gateCount}} gates require evaluation:
|
|
18
|
+
|
|
19
|
+
{{gateList}}
|
|
20
|
+
|
|
21
|
+
## Execution Protocol
|
|
22
|
+
|
|
23
|
+
1. **Dispatch all gates** using `subagent` in parallel mode. Each subagent prompt is provided below.
|
|
24
|
+
2. **Wait for all subagents** to complete.
|
|
25
|
+
3. **Verify each gate wrote its result** by checking that `gsd_save_gate_result` was called for each gate ID.
|
|
26
|
+
4. **Report the batch outcome** — which gates passed, which flagged concerns, and which were omitted as not applicable.
|
|
27
|
+
|
|
28
|
+
Gate agents may return `verdict: "omitted"` if the gate question is not applicable to this slice (e.g., no auth surface for Q3, no existing requirements touched for Q4). This is expected for simple slices.
|
|
29
|
+
|
|
30
|
+
## Subagent Prompts
|
|
31
|
+
|
|
32
|
+
{{subagentPrompts}}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
Complete slice {{sliceId}} ("{{sliceTitle}}") of milestone {{milestoneId}}. Your working directory is `{{workingDirectory}}` — all file operations must use this path. All tasks are done. Your slice summary is the primary record of what was built — downstream agents (reassess-roadmap, future slice researchers) read it to understand what this slice delivered and what to watch out for. Use the **Slice Summary** and **UAT** output templates below to understand the expected structure. {{skillActivation}} Call `gsd_slice_complete` to record completion — the tool writes `{{sliceId}}-SUMMARY.md`, `{{sliceId}}-UAT.md`, and toggles the roadmap checkbox atomically. Fill the `UAT Type` plus `Not Proven By This UAT` sections explicitly in `uatContent` so the artifact states what class of acceptance it covers and what still remains unproven. Review task summaries for `key_decisions` and ensure any significant ones are in `.gsd/DECISIONS.md`. Do not commit or merge manually — the system handles this after the unit completes.
|
|
1
|
+
Complete slice {{sliceId}} ("{{sliceTitle}}") of milestone {{milestoneId}}. Your working directory is `{{workingDirectory}}` — all file operations must use this path. All tasks are done. Your slice summary is the primary record of what was built — downstream agents (reassess-roadmap, future slice researchers) read it to understand what this slice delivered and what to watch out for. Use the **Slice Summary** and **UAT** output templates below to understand the expected structure. {{skillActivation}} Call `gsd_slice_complete` to record completion — the tool writes `{{sliceId}}-SUMMARY.md`, `{{sliceId}}-UAT.md`, and toggles the roadmap checkbox atomically. Fill the `UAT Type` plus `Not Proven By This UAT` sections explicitly in `uatContent` so the artifact states what class of acceptance it covers and what still remains unproven. Review task summaries for `key_decisions` and ensure any significant ones are in `.gsd/DECISIONS.md`. If the slice involved runtime behavior, fill the Operational Readiness section (Q8) in the summary: health signal, failure signal, recovery procedure, and monitoring gaps. Omit for simple slices. Do not commit or merge manually — the system handles this after the unit completes.
|
|
2
2
|
|
|
3
3
|
{{inlinedTemplates}}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
Execute the next task: {{taskId}} ("{{taskTitle}}") in slice {{sliceId}} of milestone {{milestoneId}}. Read the task plan (`{{taskId}}-PLAN.md`), load relevant summaries from prior tasks, and execute each step. Verify must-haves when done. If the task touches UI, browser flows, DOM behavior, or user-visible web state, exercise the real flow in the browser, prefer `browser_batch` for obvious sequences, prefer `browser_assert` for explicit pass/fail verification, use `browser_diff` when an action's effect is ambiguous, and use browser diagnostics when validating async or failure-prone UI. If you made an architectural, pattern, or library decision, append it to `.gsd/DECISIONS.md`. Use the **Task Summary** output template below. Call `gsd_task_complete` to record completion (it writes the summary, toggles the checkbox, and persists to DB atomically). {{skillActivation}} If running long and not all steps are finished, stop implementing and prioritize writing a clean partial summary over attempting one more step — a recoverable handoff is more valuable than a half-finished step with no documentation. If verification fails, debug methodically: form a hypothesis and test that specific theory before changing anything, change one variable at a time, read entire functions not just the suspect line, distinguish observable facts from assumptions, and if 3+ fixes fail without progress stop and reassess your mental model — list what you know for certain, what you've ruled out, and form fresh hypotheses. Don't fix symptoms — understand why something fails before changing code.
|
|
1
|
+
Execute the next task: {{taskId}} ("{{taskTitle}}") in slice {{sliceId}} of milestone {{milestoneId}}. Read the task plan (`{{taskId}}-PLAN.md`), load relevant summaries from prior tasks, and execute each step. Verify must-haves when done. If the task touches UI, browser flows, DOM behavior, or user-visible web state, exercise the real flow in the browser, prefer `browser_batch` for obvious sequences, prefer `browser_assert` for explicit pass/fail verification, use `browser_diff` when an action's effect is ambiguous, and use browser diagnostics when validating async or failure-prone UI. If you made an architectural, pattern, or library decision, append it to `.gsd/DECISIONS.md`. Use the **Task Summary** output template below. Call `gsd_task_complete` to record completion (it writes the summary, toggles the checkbox, and persists to DB atomically). {{skillActivation}} If running long and not all steps are finished, stop implementing and prioritize writing a clean partial summary over attempting one more step — a recoverable handoff is more valuable than a half-finished step with no documentation. If verification fails, debug methodically: form a hypothesis and test that specific theory before changing anything, change one variable at a time, read entire functions not just the suspect line, distinguish observable facts from assumptions, and if 3+ fixes fail without progress stop and reassess your mental model — list what you know for certain, what you've ruled out, and form fresh hypotheses. Don't fix symptoms — understand why something fails before changing code. If the task plan includes Failure Modes, Load Profile, or Negative Tests sections, implement and verify them: handle each dependency's error/timeout/malformed paths (Q5), protect against identified 10x breakpoints (Q6), and write specified negative test cases (Q7).
|
|
2
2
|
|
|
3
3
|
{{inlinedTemplates}}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Plan milestone {{milestoneId}} ("{{milestoneTitle}}"). Read `.gsd/DECISIONS.md` if it exists — respect existing decisions. Read `.gsd/REQUIREMENTS.md` if it exists and treat Active requirements as the capability contract. If `REQUIREMENTS.md` is missing, continue in legacy compatibility mode but explicitly note missing requirement coverage. Use the **Roadmap** output template below to shape the milestone planning payload you send to `gsd_plan_milestone`. Call `gsd_plan_milestone` to persist the milestone planning fields and render `{{milestoneId}}-ROADMAP.md` from DB state. Do **not** write `{{milestoneId}}-ROADMAP.md`, `ROADMAP.md`, or other planning artifacts manually. If planning produces structural decisions, append them to `.gsd/DECISIONS.md`. {{skillActivation}}
|
|
1
|
+
Plan milestone {{milestoneId}} ("{{milestoneTitle}}"). Read `.gsd/DECISIONS.md` if it exists — respect existing decisions. Read `.gsd/REQUIREMENTS.md` if it exists and treat Active requirements as the capability contract. If `REQUIREMENTS.md` is missing, continue in legacy compatibility mode but explicitly note missing requirement coverage. Use the **Roadmap** output template below to shape the milestone planning payload you send to `gsd_plan_milestone`. Call `gsd_plan_milestone` to persist the milestone planning fields and render `{{milestoneId}}-ROADMAP.md` from DB state. Do **not** write `{{milestoneId}}-ROADMAP.md`, `ROADMAP.md`, or other planning artifacts manually. If planning produces structural decisions, append them to `.gsd/DECISIONS.md`. {{skillActivation}} Fill the Horizontal Checklist section with cross-cutting concerns considered during planning (requirements re-read, decisions re-evaluated, graceful shutdown, revenue paths, auth boundary, shared resources, reconnection). Omit for trivial milestones.
|
|
2
2
|
|
|
3
3
|
## Requirement Rules
|
|
4
4
|
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
Plan slice {{sliceId}} ("{{sliceTitle}}") of milestone {{milestoneId}}. Read `.gsd/DECISIONS.md` if it exists — respect existing decisions. Read `.gsd/REQUIREMENTS.md` if it exists — identify which Active requirements the roadmap says this slice owns or supports, and ensure the plan delivers them. Read the roadmap boundary map, any existing context/research files, and dependency summaries. Use the **Slice Plan** and **Task Plan** output templates below. Decompose into tasks with must-haves. Fill the `Proof Level` and `Integration Closure` sections truthfully so the plan says what class of proof this slice really delivers and what end-to-end wiring still remains. Call `gsd_plan_slice` to persist the slice plan — the tool writes `{{sliceId}}-PLAN.md` and individual `T##-PLAN.md` files to disk and persists to DB. Do **not** write plan files manually — use the DB-backed tool so state stays consistent. If planning produces structural decisions, call `gsd_decision_save` for each — the tool auto-assigns IDs and regenerates `.gsd/DECISIONS.md` automatically. {{skillActivation}} Before finishing, self-audit the plan: every must-have maps to at least one task, every task has complete sections (steps, must-haves, verification, observability impact, inputs, and expected output), task ordering is consistent with no circular references, every pair of artifacts that must connect has an explicit wiring step, task scope targets 2–5 steps and 3–8 files (6–8 steps or 8–10 files — consider splitting; 10+ steps or 12+ files — must split), the plan honors locked decisions from context/research/decisions artifacts, the proof-level wording does not overclaim live integration if only fixture/contract proof is planned, every Active requirement this slice owns has at least one task with verification that proves it is met, and every task produces real user-facing progress — if the slice has a UI surface at least one task builds the real UI, if it has an API at least one task connects it to a real data source, and showing the completed result to a non-technical stakeholder would demonstrate real product progress rather than developer artifacts.
|
|
1
|
+
Plan slice {{sliceId}} ("{{sliceTitle}}") of milestone {{milestoneId}}. Read `.gsd/DECISIONS.md` if it exists — respect existing decisions. Read `.gsd/REQUIREMENTS.md` if it exists — identify which Active requirements the roadmap says this slice owns or supports, and ensure the plan delivers them. Read the roadmap boundary map, any existing context/research files, and dependency summaries. Use the **Slice Plan** and **Task Plan** output templates below. Decompose into tasks with must-haves. Fill the `Proof Level` and `Integration Closure` sections truthfully so the plan says what class of proof this slice really delivers and what end-to-end wiring still remains. Call `gsd_plan_slice` to persist the slice plan — the tool writes `{{sliceId}}-PLAN.md` and individual `T##-PLAN.md` files to disk and persists to DB. Do **not** write plan files manually — use the DB-backed tool so state stays consistent. If planning produces structural decisions, call `gsd_decision_save` for each — the tool auto-assigns IDs and regenerates `.gsd/DECISIONS.md` automatically. {{skillActivation}} Before finishing, self-audit the plan: every must-have maps to at least one task, every task has complete sections (steps, must-haves, verification, observability impact, inputs, and expected output), task ordering is consistent with no circular references, every pair of artifacts that must connect has an explicit wiring step, task scope targets 2–5 steps and 3–8 files (6–8 steps or 8–10 files — consider splitting; 10+ steps or 12+ files — must split), the plan honors locked decisions from context/research/decisions artifacts, the proof-level wording does not overclaim live integration if only fixture/contract proof is planned, every Active requirement this slice owns has at least one task with verification that proves it is met, and every task produces real user-facing progress — if the slice has a UI surface at least one task builds the real UI, if it has an API at least one task connects it to a real data source, and showing the completed result to a non-technical stakeholder would demonstrate real product progress rather than developer artifacts, and quality gate coverage — for non-trivial slices, Threat Surface (Q3: abuse, data exposure, input trust) and Requirement Impact (Q4: requirements touched, re-verify, decisions revisited) sections are present. For non-trivial tasks, Failure Modes (Q5), Load Profile (Q6), and Negative Tests (Q7) are filled in task plans.
|
|
2
2
|
|
|
3
3
|
{{inlinedTemplates}}
|
|
@@ -47,7 +47,7 @@ Then:
|
|
|
47
47
|
2. {{skillActivation}}
|
|
48
48
|
3. Create the roadmap: decompose into demoable vertical slices — as many as the work genuinely needs, no more. A simple feature might be 1 slice. Don't decompose for decomposition's sake.
|
|
49
49
|
4. Order by risk (high-risk first)
|
|
50
|
-
5. Call `gsd_plan_milestone` to persist the milestone planning fields
|
|
50
|
+
5. Call `gsd_plan_milestone` to persist the milestone planning fields, slice rows, and **horizontal checklist** in the DB-backed planning path. Do **not** write `{{outputPath}}`, `ROADMAP.md`, or other planning artifacts manually — the planning tool owns roadmap rendering and persistence.
|
|
51
51
|
6. If planning produced structural decisions (e.g. slice ordering rationale, technology choices, scope exclusions), call `gsd_decision_save` for each decision — the tool auto-assigns IDs and regenerates `.gsd/DECISIONS.md` automatically.
|
|
52
52
|
|
|
53
53
|
## Requirement Mapping Rules
|
|
@@ -57,14 +57,18 @@ Then:
|
|
|
57
57
|
- Include `Observability / Diagnostics` for backend, integration, async, stateful, or UI slices where failure diagnosis matters.
|
|
58
58
|
- Fill `Proof Level` and `Integration Closure` when the slice crosses runtime boundaries or has meaningful integration concerns.
|
|
59
59
|
- **Omit these sections entirely for simple slices** where they would all be "none" or trivially obvious.
|
|
60
|
-
5.
|
|
60
|
+
5. **Quality gates** — for non-trivial slices, fill the Threat Surface (Q3) and Requirement Impact (Q4) sections in the slice plan:
|
|
61
|
+
- **Threat Surface:** Identify abuse scenarios, data exposure risks, and input trust boundaries. Required when the slice handles user input, authentication, authorization, or sensitive data. Omit entirely for internal refactoring or simple changes.
|
|
62
|
+
- **Requirement Impact:** List which existing requirements this slice touches, what must be re-verified after shipping, and which prior decisions should be reconsidered. Omit entirely if no existing requirements are affected.
|
|
63
|
+
- For each task in a non-trivial slice, fill Failure Modes (Q5), Load Profile (Q6), and Negative Tests (Q7) in the task plan when the task has external dependencies, shared resources, or non-trivial input handling. Omit for simple tasks.
|
|
64
|
+
6. Decompose the slice into tasks, each fitting one context window. Each task needs:
|
|
61
65
|
- a concrete, action-oriented title
|
|
62
66
|
- the inline task entry fields defined in the plan.md template (Why / Files / Do / Verify / Done when)
|
|
63
67
|
- a matching task plan file with description, steps, must-haves, verification, inputs, and expected output
|
|
64
68
|
- **Inputs and Expected Output must list concrete backtick-wrapped file paths** (e.g. `` `src/types.ts` ``). These are machine-parsed to derive task dependencies — vague prose without paths breaks parallel execution. Every task must have at least one output file path.
|
|
65
69
|
- Observability Impact section **only if the task touches runtime boundaries, async flows, or error paths** — omit it otherwise
|
|
66
|
-
|
|
67
|
-
|
|
70
|
+
7. **Persist planning state through `gsd_plan_slice`.** Call it with the full slice planning payload (goal, demo, must-haves, verification, tasks, and metadata). The tool inserts all tasks in the same transaction, writes to the DB, and renders `{{outputPath}}` and `{{slicePath}}/tasks/T##-PLAN.md` files automatically. Do **not** call `gsd_plan_task` separately — `gsd_plan_slice` handles task persistence. Do **not** rely on direct `PLAN.md` writes as the source of truth; the DB-backed tool is the canonical write path for slice and task planning state.
|
|
71
|
+
8. **Self-audit the plan.** Walk through each check — if any fail, fix the plan files before moving on:
|
|
68
72
|
- **Completion semantics:** If every task were completed exactly as written, the slice goal/demo should actually be true.
|
|
69
73
|
- **Requirement coverage:** Every must-have in the slice maps to at least one task. No must-have is orphaned. If `REQUIREMENTS.md` exists, every Active requirement this slice owns maps to at least one task.
|
|
70
74
|
- **Task completeness:** Every task has steps, must-haves, verification, inputs, and expected output — none are blank or vague. Inputs and Expected Output list backtick-wrapped file paths, not prose descriptions.
|
|
@@ -72,6 +76,7 @@ Then:
|
|
|
72
76
|
- **Key links planned:** For every pair of artifacts that must connect, there is an explicit step that wires them.
|
|
73
77
|
- **Scope sanity:** Target 2–5 steps and 3–8 files per task. 10+ steps or 12+ files — must split. Each task must be completable in a single fresh context window.
|
|
74
78
|
- **Feature completeness:** Every task produces real, user-facing progress — not just internal scaffolding.
|
|
79
|
+
- **Quality gate coverage:** For non-trivial slices, Threat Surface and Requirement Impact sections are present and specific (not placeholder text). For non-trivial tasks, Failure Modes, Load Profile, and Negative Tests are addressed in the task plan.
|
|
75
80
|
10. If planning produced structural decisions, append them to `.gsd/DECISIONS.md`
|
|
76
81
|
11. {{commitInstruction}}
|
|
77
82
|
|
|
@@ -36,6 +36,9 @@ Ask yourself:
|
|
|
36
36
|
- Did assumptions in remaining slice descriptions turn out wrong?
|
|
37
37
|
- If `.gsd/REQUIREMENTS.md` exists: did this slice validate, invalidate, defer, block, or newly surface requirements?
|
|
38
38
|
- If `.gsd/REQUIREMENTS.md` exists: does the remaining roadmap still provide credible coverage for Active requirements, including launchability, primary user loop, continuity, and failure visibility where relevant?
|
|
39
|
+
- Are the Threat Surface and Requirement Impact sections in completed slice plans still accurate for remaining slices?
|
|
40
|
+
- Did this slice's Operational Readiness reveal monitoring gaps that remaining slices should address?
|
|
41
|
+
- Should any Horizontal Checklist items be updated based on what was actually built?
|
|
39
42
|
|
|
40
43
|
### Success-Criterion Coverage Check
|
|
41
44
|
|
|
@@ -32,7 +32,7 @@ Consider these captures when rewriting the remaining tasks — they represent th
|
|
|
32
32
|
|
|
33
33
|
1. Read the blocker task summary carefully. Understand exactly what was discovered and why it blocks the current plan.
|
|
34
34
|
2. Analyze the remaining `[ ]` tasks in the slice plan. Determine which are still valid, which need modification, and which should be replaced.
|
|
35
|
-
3. **Persist replan state through `gsd_replan_slice`.** Call it with: `milestoneId`, `sliceId`, `blockerTaskId`, `blockerDescription`, `whatChanged`, `updatedTasks` (array of task objects with taskId, title, description, estimate, files, verify, inputs, expectedOutput), `removedTaskIds` (array of task ID strings). The tool structurally enforces preservation of completed tasks, writes replan history to the DB, re-renders `{{planPath}}`, and renders `{{replanPath}}`.
|
|
35
|
+
3. **Persist replan state through `gsd_replan_slice`.** Call it with: `milestoneId`, `sliceId`, `blockerTaskId`, `blockerDescription`, `whatChanged`, `updatedTasks` (array of task objects with taskId, title, description, estimate, files, verify, inputs, expectedOutput), `removedTaskIds` (array of task ID strings). The tool structurally enforces preservation of completed tasks, writes replan history to the DB, re-renders `{{planPath}}`, and renders `{{replanPath}}`. Preserve or update the Threat Surface and Requirement Impact sections if the replan changes the slice's security posture or requirement coverage.
|
|
36
36
|
4. If any incomplete task had a `T0x-PLAN.md`, remove or rewrite it to match the new task description.
|
|
37
37
|
5. Do not commit manually — the system auto-commits your changes after this unit completes.
|
|
38
38
|
|
|
@@ -378,6 +378,34 @@ export function ensureGsdSymlink(projectPath: string): string {
|
|
|
378
378
|
return localGsd;
|
|
379
379
|
}
|
|
380
380
|
|
|
381
|
+
// Guard: If projectPath is a plain subdirectory (not a worktree) of a git
|
|
382
|
+
// repo that already has a .gsd at the git root, do not create a duplicate
|
|
383
|
+
// symlink in the subdirectory — that causes `.gsd 2` collision variants on
|
|
384
|
+
// macOS (#2380). Worktrees are excluded because they legitimately need their
|
|
385
|
+
// own .gsd symlink pointing at the shared external state dir.
|
|
386
|
+
if (!inWorktree) {
|
|
387
|
+
try {
|
|
388
|
+
const gitRoot = resolveGitRoot(projectPath);
|
|
389
|
+
const normalizedProject = canonicalizeExistingPath(projectPath);
|
|
390
|
+
const normalizedRoot = canonicalizeExistingPath(gitRoot);
|
|
391
|
+
if (normalizedProject !== normalizedRoot) {
|
|
392
|
+
const rootGsd = join(gitRoot, ".gsd");
|
|
393
|
+
if (existsSync(rootGsd)) {
|
|
394
|
+
try {
|
|
395
|
+
const rootStat = lstatSync(rootGsd);
|
|
396
|
+
if (rootStat.isSymbolicLink() || rootStat.isDirectory()) {
|
|
397
|
+
return rootStat.isSymbolicLink() ? realpathSync(rootGsd) : rootGsd;
|
|
398
|
+
}
|
|
399
|
+
} catch {
|
|
400
|
+
// Fall through to normal logic if we can't stat root .gsd
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
} catch {
|
|
405
|
+
// If git root detection fails, fall through to normal logic
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
381
409
|
// Clean up macOS numbered collision variants (.gsd 2, .gsd 3, etc.) before
|
|
382
410
|
// any existence checks — otherwise they accumulate and confuse state (#2205).
|
|
383
411
|
cleanNumberedGsdVariants(projectPath);
|
|
@@ -41,8 +41,8 @@ export function expandDependencies(deps: string[]): string[] {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
function extractSlicesSection(content: string): string {
|
|
44
|
-
// Match "## Slices", "## Slice Overview", "## Slice Table", etc.
|
|
45
|
-
const headingMatch = /^## Slice(?:s| Overview| Table| Summary| Status)\b.*$/m.exec(content);
|
|
44
|
+
// Match "## Slices", "## Slice Overview", "## Slice Table", "## Slice Roadmap", etc.
|
|
45
|
+
const headingMatch = /^## Slice(?:s| Overview| Table| Summary| Status| Roadmap)\b.*$/m.exec(content);
|
|
46
46
|
if (!headingMatch || headingMatch.index == null) return "";
|
|
47
47
|
|
|
48
48
|
const start = headingMatch.index + headingMatch[0].length;
|
|
@@ -25,7 +25,6 @@ import { truncateWithEllipsis } from "../shared/format-utils.js";
|
|
|
25
25
|
import { nativeParseJsonlTail } from "./native-parser-bridge.js";
|
|
26
26
|
import { MAX_JSONL_BYTES, parseJSONL } from "./jsonl-utils.js";
|
|
27
27
|
import { nativeWorkingTreeStatus, nativeDiffStat } from "./native-git-bridge.js";
|
|
28
|
-
import { getAutoWorktreePath } from "./auto-worktree.js";
|
|
29
28
|
|
|
30
29
|
// ─── Types ────────────────────────────────────────────────────────────────────
|
|
31
30
|
|
|
@@ -295,17 +294,13 @@ export function synthesizeCrashRecovery(
|
|
|
295
294
|
* Deep diagnostic from any JSONL source (activity log or session file).
|
|
296
295
|
* Replaces the old shallow getLastActivityDiagnostic().
|
|
297
296
|
*/
|
|
298
|
-
export function getDeepDiagnostic(basePath: string): string | null {
|
|
299
|
-
// Try worktree activity logs first if
|
|
297
|
+
export function getDeepDiagnostic(basePath: string, worktreePath?: string): string | null {
|
|
298
|
+
// Try worktree activity logs first if a worktree path is provided
|
|
300
299
|
let trace: ExecutionTrace | null = null;
|
|
301
300
|
try {
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
if (wtPath) {
|
|
306
|
-
const wtActivityDir = join(gsdRoot(wtPath), "activity");
|
|
307
|
-
trace = readLastActivityLog(wtActivityDir);
|
|
308
|
-
}
|
|
301
|
+
if (worktreePath) {
|
|
302
|
+
const wtActivityDir = join(gsdRoot(worktreePath), "activity");
|
|
303
|
+
trace = readLastActivityLog(wtActivityDir);
|
|
309
304
|
}
|
|
310
305
|
} catch { /* non-fatal — fall through to root */ }
|
|
311
306
|
|
|
@@ -323,7 +318,7 @@ export function getDeepDiagnostic(basePath: string): string | null {
|
|
|
323
318
|
* Read the active milestone ID directly from STATE.md without async deriveState().
|
|
324
319
|
* Looks for `**Active Milestone:** M001` pattern.
|
|
325
320
|
*/
|
|
326
|
-
function readActiveMilestoneId(basePath: string): string | null {
|
|
321
|
+
export function readActiveMilestoneId(basePath: string): string | null {
|
|
327
322
|
try {
|
|
328
323
|
const statePath = join(gsdRoot(basePath), "STATE.md");
|
|
329
324
|
if (!existsSync(statePath)) return null;
|
|
@@ -167,6 +167,56 @@ function ensureExitHandler(_gsdDir: string): void {
|
|
|
167
167
|
});
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
// ─── Lock Acquisition Helpers ───────────────────────────────────────────────
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Create the onCompromised callback for proper-lockfile.
|
|
174
|
+
*
|
|
175
|
+
* proper-lockfile fires onCompromised when it detects mtime drift (system sleep,
|
|
176
|
+
* event loop stall, etc.). The default handler throws inside setTimeout — an
|
|
177
|
+
* uncaught exception that crashes or corrupts process state.
|
|
178
|
+
*
|
|
179
|
+
* False-positive suppression (#1362): If we're still within the stale window
|
|
180
|
+
* (30 min since acquisition), the mtime mismatch is from an event loop stall
|
|
181
|
+
* during a long LLM call — not a real takeover. Log and continue.
|
|
182
|
+
*
|
|
183
|
+
* PID ownership check (#1578): Past the stale window, check if the lock file
|
|
184
|
+
* still contains our PID before declaring compromise. Retry reads tolerate
|
|
185
|
+
* transient filesystem hiccups (NFS/CIFS latency, APFS snapshots, etc.) (#2324).
|
|
186
|
+
*/
|
|
187
|
+
function createLockCompromisedHandler(lockFilePath: string): () => void {
|
|
188
|
+
return () => {
|
|
189
|
+
const elapsed = Date.now() - _lockAcquiredAt;
|
|
190
|
+
if (elapsed < 1_800_000) {
|
|
191
|
+
process.stderr.write(
|
|
192
|
+
`[gsd] Lock heartbeat caught up after ${Math.round(elapsed / 1000)}s — long LLM call, no action needed.\n`,
|
|
193
|
+
);
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
const existing = readExistingLockDataWithRetry(lockFilePath);
|
|
197
|
+
if (existing && existing.pid === process.pid) {
|
|
198
|
+
process.stderr.write(
|
|
199
|
+
`[gsd] Lock heartbeat mismatch after ${Math.round(elapsed / 1000)}s — lock file still owned by PID ${process.pid}, treating as false positive.\n`,
|
|
200
|
+
);
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
_lockCompromised = true;
|
|
204
|
+
_releaseFunction = null;
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Assign module-level lock state after a successful lock acquisition.
|
|
210
|
+
*/
|
|
211
|
+
function assignLockState(basePath: string, release: () => void, lockFilePath: string): void {
|
|
212
|
+
_releaseFunction = release;
|
|
213
|
+
_lockedPath = basePath;
|
|
214
|
+
_lockPid = process.pid;
|
|
215
|
+
_lockCompromised = false;
|
|
216
|
+
_lockAcquiredAt = Date.now();
|
|
217
|
+
_snapshotLockPath = lockFilePath;
|
|
218
|
+
}
|
|
219
|
+
|
|
170
220
|
// ─── Public API ─────────────────────────────────────────────────────────────
|
|
171
221
|
|
|
172
222
|
/**
|
|
@@ -226,43 +276,10 @@ export function acquireSessionLock(basePath: string): SessionLockResult {
|
|
|
226
276
|
realpath: false,
|
|
227
277
|
stale: 1_800_000, // 30 minutes — safe for laptop sleep / long event loop stalls
|
|
228
278
|
update: 10_000, // Update lock mtime every 10s to prove liveness
|
|
229
|
-
onCompromised: ()
|
|
230
|
-
// proper-lockfile detected mtime drift (system sleep, event loop stall, etc.).
|
|
231
|
-
// Default handler throws inside setTimeout — an uncaught exception that crashes
|
|
232
|
-
// or corrupts process state.
|
|
233
|
-
//
|
|
234
|
-
// False-positive suppression (#1362): If we're still within the stale window
|
|
235
|
-
// (30 min since acquisition), the mtime mismatch is from an event loop stall
|
|
236
|
-
// during a long LLM call — not a real takeover. Log and continue.
|
|
237
|
-
const elapsed = Date.now() - _lockAcquiredAt;
|
|
238
|
-
if (elapsed < 1_800_000) {
|
|
239
|
-
process.stderr.write(
|
|
240
|
-
`[gsd] Lock heartbeat caught up after ${Math.round(elapsed / 1000)}s — long LLM call, no action needed.\n`,
|
|
241
|
-
);
|
|
242
|
-
return; // Suppress false positive
|
|
243
|
-
}
|
|
244
|
-
// Past the stale window — check if the lock file still belongs to us before
|
|
245
|
-
// declaring compromise (#1578). If our PID still owns the metadata, this is
|
|
246
|
-
// a false positive from a very long event loop stall (e.g. subagent execution).
|
|
247
|
-
const existing = readExistingLockData(lp);
|
|
248
|
-
if (existing && existing.pid === process.pid) {
|
|
249
|
-
process.stderr.write(
|
|
250
|
-
`[gsd] Lock heartbeat mismatch after ${Math.round(elapsed / 1000)}s — lock file still owned by PID ${process.pid}, treating as false positive.\n`,
|
|
251
|
-
);
|
|
252
|
-
return; // Our PID still owns the lock file — no real takeover
|
|
253
|
-
}
|
|
254
|
-
// Lock file is gone or owned by another PID — real compromise
|
|
255
|
-
_lockCompromised = true;
|
|
256
|
-
_releaseFunction = null;
|
|
257
|
-
},
|
|
279
|
+
onCompromised: createLockCompromisedHandler(lp),
|
|
258
280
|
});
|
|
259
281
|
|
|
260
|
-
|
|
261
|
-
_lockedPath = basePath;
|
|
262
|
-
_lockPid = process.pid;
|
|
263
|
-
_lockCompromised = false;
|
|
264
|
-
_lockAcquiredAt = Date.now();
|
|
265
|
-
_snapshotLockPath = lp; // Snapshot the resolved path for consistent access (#1363)
|
|
282
|
+
assignLockState(basePath, release, lp);
|
|
266
283
|
|
|
267
284
|
// Safety net: clean up lock dir on process exit if _releaseFunction
|
|
268
285
|
// wasn't called (e.g., normal exit after clean completion) (#1245).
|
|
@@ -290,35 +307,9 @@ export function acquireSessionLock(basePath: string): SessionLockResult {
|
|
|
290
307
|
realpath: false,
|
|
291
308
|
stale: 1_800_000, // 30 minutes — match primary lock settings
|
|
292
309
|
update: 10_000,
|
|
293
|
-
onCompromised: ()
|
|
294
|
-
// Same false-positive suppression as the primary lock (#1512).
|
|
295
|
-
// Without this, the retry path fires _lockCompromised unconditionally
|
|
296
|
-
// on benign mtime drift (laptop sleep, heavy LLM event loop stalls).
|
|
297
|
-
const elapsed = Date.now() - _lockAcquiredAt;
|
|
298
|
-
if (elapsed < 1_800_000) {
|
|
299
|
-
process.stderr.write(
|
|
300
|
-
`[gsd] Lock heartbeat caught up after ${Math.round(elapsed / 1000)}s — long LLM call, no action needed.\n`,
|
|
301
|
-
);
|
|
302
|
-
return;
|
|
303
|
-
}
|
|
304
|
-
// Check PID ownership before declaring compromise (#1578)
|
|
305
|
-
const existing = readExistingLockData(lp);
|
|
306
|
-
if (existing && existing.pid === process.pid) {
|
|
307
|
-
process.stderr.write(
|
|
308
|
-
`[gsd] Lock heartbeat mismatch after ${Math.round(elapsed / 1000)}s — lock file still owned by PID ${process.pid}, treating as false positive.\n`,
|
|
309
|
-
);
|
|
310
|
-
return;
|
|
311
|
-
}
|
|
312
|
-
_lockCompromised = true;
|
|
313
|
-
_releaseFunction = null;
|
|
314
|
-
},
|
|
310
|
+
onCompromised: createLockCompromisedHandler(lp),
|
|
315
311
|
});
|
|
316
|
-
|
|
317
|
-
_lockedPath = basePath;
|
|
318
|
-
_lockPid = process.pid;
|
|
319
|
-
_lockCompromised = false;
|
|
320
|
-
_lockAcquiredAt = Date.now();
|
|
321
|
-
_snapshotLockPath = lp; // Snapshot for retry path too (#1363)
|
|
312
|
+
assignLockState(basePath, release, lp);
|
|
322
313
|
|
|
323
314
|
// Safety net — uses centralized handler to avoid double-registration
|
|
324
315
|
ensureExitHandler(gsdDir);
|
|
@@ -413,7 +404,8 @@ export function getSessionLockStatus(basePath: string): SessionLockStatus {
|
|
|
413
404
|
// onCompromised fired from benign mtime drift (laptop sleep, event loop stall
|
|
414
405
|
// beyond the stale window). Attempt re-acquisition instead of giving up.
|
|
415
406
|
const lp = lockPath(basePath);
|
|
416
|
-
|
|
407
|
+
// Retry reads to tolerate transient filesystem hiccups (#2324).
|
|
408
|
+
const existing = readExistingLockDataWithRetry(lp);
|
|
417
409
|
if (existing && existing.pid === process.pid) {
|
|
418
410
|
// Lock file still ours — try to re-acquire the OS lock
|
|
419
411
|
try {
|
|
@@ -565,6 +557,42 @@ function readExistingLockData(lp: string): SessionLockData | null {
|
|
|
565
557
|
}
|
|
566
558
|
}
|
|
567
559
|
|
|
560
|
+
/**
|
|
561
|
+
* Retry-tolerant variant of readExistingLockData for use in onCompromised and
|
|
562
|
+
* other paths where a transient filesystem hiccup (NFS/CIFS latency, macOS APFS
|
|
563
|
+
* snapshot, concurrent process briefly holding the file) should NOT be treated
|
|
564
|
+
* as "lock file gone" (#2324).
|
|
565
|
+
*
|
|
566
|
+
* Retries up to `maxAttempts` times with `delayMs` between each attempt.
|
|
567
|
+
* Only returns null when ALL retries fail to read valid data.
|
|
568
|
+
*/
|
|
569
|
+
export interface RetryOptions {
|
|
570
|
+
maxAttempts?: number;
|
|
571
|
+
delayMs?: number;
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
export function readExistingLockDataWithRetry(
|
|
575
|
+
lp: string,
|
|
576
|
+
options?: RetryOptions,
|
|
577
|
+
): SessionLockData | null {
|
|
578
|
+
const maxAttempts = options?.maxAttempts ?? 3;
|
|
579
|
+
const delayMs = options?.delayMs ?? 200;
|
|
580
|
+
|
|
581
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
582
|
+
const data = readExistingLockData(lp);
|
|
583
|
+
if (data !== null) return data;
|
|
584
|
+
if (attempt < maxAttempts) {
|
|
585
|
+
// Synchronous busy-wait — onCompromised runs in a sync callback context
|
|
586
|
+
// and the delays are short (200ms default).
|
|
587
|
+
const start = Date.now();
|
|
588
|
+
while (Date.now() - start < delayMs) {
|
|
589
|
+
// busy-wait
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
return null;
|
|
594
|
+
}
|
|
595
|
+
|
|
568
596
|
function isPidAlive(pid: number): boolean {
|
|
569
597
|
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
570
598
|
if (pid === process.pid) return false;
|
|
@@ -40,6 +40,7 @@ import { nativeBatchParseGsdFiles, type BatchParsedFile } from './native-parser-
|
|
|
40
40
|
import { join, resolve } from 'path';
|
|
41
41
|
import { existsSync, readdirSync } from 'node:fs';
|
|
42
42
|
import { debugCount, debugTime } from './debug-logger.js';
|
|
43
|
+
import { extractVerdict } from './verdict-parser.js';
|
|
43
44
|
|
|
44
45
|
import {
|
|
45
46
|
isDbAvailable,
|
|
@@ -50,6 +51,7 @@ import {
|
|
|
50
51
|
getSlice,
|
|
51
52
|
insertMilestone,
|
|
52
53
|
updateTaskStatus,
|
|
54
|
+
getPendingSliceGateCount,
|
|
53
55
|
type MilestoneRow,
|
|
54
56
|
type SliceRow,
|
|
55
57
|
type TaskRow,
|
|
@@ -91,11 +93,8 @@ export function isMilestoneComplete(roadmap: Roadmap): boolean {
|
|
|
91
93
|
* after remediation slices are executed.
|
|
92
94
|
*/
|
|
93
95
|
export function isValidationTerminal(validationContent: string): boolean {
|
|
94
|
-
const
|
|
95
|
-
if (!
|
|
96
|
-
const verdict = match[1].match(/verdict:\s*(\S+)/);
|
|
97
|
-
if (!verdict) return false;
|
|
98
|
-
const v = verdict[1] === 'passed' ? 'pass' : verdict[1];
|
|
96
|
+
const v = extractVerdict(validationContent);
|
|
97
|
+
if (!v) return false;
|
|
99
98
|
// 'pass' and 'needs-attention' are always terminal.
|
|
100
99
|
// 'needs-remediation' is treated as terminal to prevent infinite loops
|
|
101
100
|
// when no remediation slices exist in the roadmap (#832). The validation
|
|
@@ -711,6 +710,22 @@ export async function deriveStateFromDb(basePath: string): Promise<GSDState> {
|
|
|
711
710
|
}
|
|
712
711
|
}
|
|
713
712
|
|
|
713
|
+
// ── Quality gate evaluation check ──────────────────────────────────
|
|
714
|
+
// If slice-scoped gates (Q3/Q4) are still pending, pause before execution
|
|
715
|
+
// so the gate-evaluate dispatch rule can run parallel sub-agents.
|
|
716
|
+
// Slices with zero gate rows (pre-feature or simple) skip straight through.
|
|
717
|
+
const pendingGateCount = getPendingSliceGateCount(activeMilestone.id, activeSlice.id);
|
|
718
|
+
if (pendingGateCount > 0) {
|
|
719
|
+
return {
|
|
720
|
+
activeMilestone, activeSlice, activeTask: null,
|
|
721
|
+
phase: 'evaluating-gates',
|
|
722
|
+
recentDecisions: [], blockers: [],
|
|
723
|
+
nextAction: `Evaluate ${pendingGateCount} quality gate(s) for ${activeSlice.id} before execution.`,
|
|
724
|
+
registry, requirements,
|
|
725
|
+
progress: { milestones: milestoneProgress, slices: sliceProgress, tasks: taskProgress },
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
|
|
714
729
|
// ── Blocker detection: check completed tasks for blocker_discovered ──
|
|
715
730
|
const completedTasks = tasks.filter(t => isStatusDone(t.status));
|
|
716
731
|
let blockerTaskId: string | null = null;
|
|
@@ -1280,6 +1295,24 @@ export async function _deriveStateImpl(basePath: string): Promise<GSDState> {
|
|
|
1280
1295
|
}
|
|
1281
1296
|
|
|
1282
1297
|
const slicePlan = parsePlan(slicePlanContent);
|
|
1298
|
+
|
|
1299
|
+
// ── Reconcile stale task status for filesystem-based projects (#2514) ──
|
|
1300
|
+
// Heading-style tasks (### T01:) are always parsed as done=false by
|
|
1301
|
+
// parsePlan because the heading syntax has no checkbox. When the agent
|
|
1302
|
+
// writes a SUMMARY file but the plan's heading isn't converted to a
|
|
1303
|
+
// checkbox, the task appears incomplete forever — causing infinite
|
|
1304
|
+
// re-dispatch. Reconcile by checking SUMMARY files on disk.
|
|
1305
|
+
for (const t of slicePlan.tasks) {
|
|
1306
|
+
if (t.done) continue;
|
|
1307
|
+
const summaryPath = resolveTaskFile(basePath, activeMilestone.id, activeSlice.id, t.id, "SUMMARY");
|
|
1308
|
+
if (summaryPath && existsSync(summaryPath)) {
|
|
1309
|
+
t.done = true;
|
|
1310
|
+
process.stderr.write(
|
|
1311
|
+
`gsd-reconcile: task ${activeMilestone.id}/${activeSlice.id}/${t.id} has SUMMARY on disk but plan shows incomplete — marking done (#2514)\n`,
|
|
1312
|
+
);
|
|
1313
|
+
}
|
|
1314
|
+
}
|
|
1315
|
+
|
|
1283
1316
|
const taskProgress = {
|
|
1284
1317
|
done: slicePlan.tasks.filter(t => t.done).length,
|
|
1285
1318
|
total: slicePlan.tasks.length,
|
|
@@ -49,6 +49,14 @@ completed_at: {{date}}
|
|
|
49
49
|
|
|
50
50
|
- {{requirementId}}: {{fromStatus}} → {{toStatus}} — {{evidence}}
|
|
51
51
|
|
|
52
|
+
## Decision Re-evaluation
|
|
53
|
+
|
|
54
|
+
<!-- Review decisions from this milestone. OMIT if no decisions need re-evaluation. -->
|
|
55
|
+
|
|
56
|
+
| Decision | Original Rationale | Still Valid? | Action |
|
|
57
|
+
|----------|-------------------|-------------|--------|
|
|
58
|
+
| {{decisionId}} | {{originalRationale}} | {{yes/no/partially}} | {{keep/revise/supersede}} |
|
|
59
|
+
|
|
52
60
|
## Forward Intelligence
|
|
53
61
|
|
|
54
62
|
<!-- Write what you wish you'd known at the start of this milestone.
|
|
@@ -8,6 +8,22 @@
|
|
|
8
8
|
- {{mustHave}}
|
|
9
9
|
- {{mustHave}}
|
|
10
10
|
|
|
11
|
+
## Threat Surface
|
|
12
|
+
|
|
13
|
+
<!-- Q3: How can this be exploited? OMIT ENTIRELY for simple slices with no auth, user input, or data exposure. -->
|
|
14
|
+
|
|
15
|
+
- **Abuse**: {{abuseScenarios — parameter tampering, replay, privilege escalation, or N/A}}
|
|
16
|
+
- **Data exposure**: {{sensitiveDataAccessible — PII, tokens, secrets, or none}}
|
|
17
|
+
- **Input trust**: {{untrustedInput — user input reaching DB/API/filesystem, or none}}
|
|
18
|
+
|
|
19
|
+
## Requirement Impact
|
|
20
|
+
|
|
21
|
+
<!-- Q4: What existing promises does this break? OMIT ENTIRELY if no existing requirements are affected. -->
|
|
22
|
+
|
|
23
|
+
- **Requirements touched**: {{requirementIds — e.g. R001, R003, or none}}
|
|
24
|
+
- **Re-verify**: {{whatMustBeRetested — e.g. login flow, API contract, or N/A}}
|
|
25
|
+
- **Decisions revisited**: {{decisionIds — e.g. D002, or none}}
|
|
26
|
+
|
|
11
27
|
## Proof Level
|
|
12
28
|
|
|
13
29
|
<!-- Omit this section entirely for simple slices where the answer is trivially obvious. -->
|
|
@@ -92,6 +92,19 @@ This milestone is complete only when all are true:
|
|
|
92
92
|
- Each "After this" line must be truthful about proof level: if only fixtures or tests prove it, say so; do not imply the user can already perform the live end-to-end behavior unless that has actually been exercised
|
|
93
93
|
-->
|
|
94
94
|
|
|
95
|
+
## Horizontal Checklist
|
|
96
|
+
|
|
97
|
+
<!-- Cross-cutting concerns across all slices. Check each that was considered.
|
|
98
|
+
OMIT ENTIRELY for trivial milestones. -->
|
|
99
|
+
|
|
100
|
+
- [ ] Every active R### re-read against new code — still fully satisfied?
|
|
101
|
+
- [ ] Every D### from prior milestones re-evaluated — still valid at new scope?
|
|
102
|
+
- [ ] Graceful shutdown / cleanup on termination verified
|
|
103
|
+
- [ ] Revenue / billing path impact assessed (or N/A)
|
|
104
|
+
- [ ] Auth boundary documented — what's protected vs public
|
|
105
|
+
- [ ] Shared resource budget confirmed — connection pools, caches, rate limits hold under peak
|
|
106
|
+
- [ ] Reconnection / retry strategy verified for every external dependency
|
|
107
|
+
|
|
95
108
|
## Boundary Map
|
|
96
109
|
|
|
97
110
|
<!-- Be specific. Name concrete outputs: API endpoints, event payloads, shared types/interfaces,
|
|
@@ -57,6 +57,15 @@ completed_at: {{date}}
|
|
|
57
57
|
|
|
58
58
|
- {{requirementIdOr_none}} — {{what changed}}
|
|
59
59
|
|
|
60
|
+
## Operational Readiness
|
|
61
|
+
|
|
62
|
+
<!-- Q8: How will ops know it's healthy/broken? OMIT ENTIRELY for simple slices with no runtime concerns. -->
|
|
63
|
+
|
|
64
|
+
- **Health signal**: {{howToConfirmHealthy — health endpoint, heartbeat log, metric, or N/A}}
|
|
65
|
+
- **Failure signal**: {{howToDetectBroken — error rate spike, alert, log pattern, or N/A}}
|
|
66
|
+
- **Recovery**: {{selfRecoverOrRestart — auto-reconnect, circuit breaker, manual restart, or N/A}}
|
|
67
|
+
- **Monitoring gaps**: {{silentFailureModes — background jobs, cache eviction, memory pressure, or none}}
|
|
68
|
+
|
|
60
69
|
## Deviations
|
|
61
70
|
|
|
62
71
|
<!-- Deviations are unplanned changes to the written plan, not ordinary debugging inside the plan's intended scope. -->
|