gsd-pi 2.26.0 → 2.27.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 +43 -6
- package/dist/cli.js +4 -2
- package/dist/headless.d.ts +3 -0
- package/dist/headless.js +136 -8
- package/dist/help-text.js +3 -0
- package/dist/loader.js +33 -4
- package/dist/resources/extensions/bg-shell/index.ts +19 -2
- package/dist/resources/extensions/bg-shell/process-manager.ts +45 -0
- package/dist/resources/extensions/bg-shell/types.ts +21 -1
- package/dist/resources/extensions/gsd/auto/session.ts +224 -0
- package/dist/resources/extensions/gsd/auto-budget.ts +32 -0
- package/dist/resources/extensions/gsd/auto-dashboard.ts +63 -10
- package/dist/resources/extensions/gsd/auto-direct-dispatch.ts +229 -0
- package/dist/resources/extensions/gsd/auto-dispatch.ts +23 -10
- package/dist/resources/extensions/gsd/auto-model-selection.ts +179 -0
- package/dist/resources/extensions/gsd/auto-observability.ts +74 -0
- package/dist/resources/extensions/gsd/auto-prompts.ts +0 -1
- package/dist/resources/extensions/gsd/auto-timeout-recovery.ts +262 -0
- package/dist/resources/extensions/gsd/auto-tool-tracking.ts +54 -0
- package/dist/resources/extensions/gsd/auto-unit-closeout.ts +46 -0
- package/dist/resources/extensions/gsd/auto-worktree-sync.ts +207 -0
- package/dist/resources/extensions/gsd/auto.ts +977 -1551
- package/dist/resources/extensions/gsd/commands.ts +3 -3
- package/dist/resources/extensions/gsd/dashboard-overlay.ts +47 -72
- package/dist/resources/extensions/gsd/doctor-proactive.ts +9 -4
- package/dist/resources/extensions/gsd/export-html.ts +1001 -0
- package/dist/resources/extensions/gsd/export.ts +49 -1
- package/dist/resources/extensions/gsd/git-service.ts +6 -0
- package/dist/resources/extensions/gsd/gitignore.ts +4 -1
- package/dist/resources/extensions/gsd/guided-flow.ts +24 -5
- package/dist/resources/extensions/gsd/index.ts +54 -1
- package/dist/resources/extensions/gsd/native-git-bridge.ts +30 -2
- package/dist/resources/extensions/gsd/observability-validator.ts +21 -0
- package/dist/resources/extensions/gsd/parallel-orchestrator.ts +231 -20
- package/dist/resources/extensions/gsd/preferences.ts +62 -1
- package/dist/resources/extensions/gsd/prompts/execute-task.md +4 -3
- package/dist/resources/extensions/gsd/prompts/system.md +1 -1
- package/dist/resources/extensions/gsd/reports.ts +510 -0
- package/dist/resources/extensions/gsd/roadmap-slices.ts +1 -1
- package/dist/resources/extensions/gsd/skills/gsd-headless/SKILL.md +178 -0
- package/dist/resources/extensions/gsd/skills/gsd-headless/references/answer-injection.md +54 -0
- package/dist/resources/extensions/gsd/skills/gsd-headless/references/commands.md +59 -0
- package/dist/resources/extensions/gsd/skills/gsd-headless/references/multi-session.md +185 -0
- package/dist/resources/extensions/gsd/state.ts +30 -0
- package/dist/resources/extensions/gsd/templates/task-summary.md +9 -0
- package/dist/resources/extensions/gsd/tests/auto-dashboard.test.ts +13 -0
- package/dist/resources/extensions/gsd/tests/continue-here.test.ts +81 -0
- package/dist/resources/extensions/gsd/tests/derive-state-db.test.ts +5 -0
- package/dist/resources/extensions/gsd/tests/derive-state-deps.test.ts +1 -0
- package/dist/resources/extensions/gsd/tests/derive-state-draft.test.ts +1 -0
- package/dist/resources/extensions/gsd/tests/derive-state.test.ts +10 -1
- package/dist/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +132 -0
- package/dist/resources/extensions/gsd/tests/doctor-proactive.test.ts +14 -0
- package/dist/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +1 -0
- package/dist/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +1 -1
- package/dist/resources/extensions/gsd/tests/native-has-changes-cache.test.ts +61 -0
- package/dist/resources/extensions/gsd/tests/network-error-fallback.test.ts +51 -1
- package/dist/resources/extensions/gsd/tests/parallel-budget-atomicity.test.ts +331 -0
- package/dist/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +298 -0
- package/dist/resources/extensions/gsd/tests/parallel-merge.test.ts +465 -0
- package/dist/resources/extensions/gsd/tests/parallel-orchestration.test.ts +39 -10
- package/dist/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +71 -0
- package/dist/resources/extensions/gsd/tests/replan-slice.test.ts +42 -0
- package/dist/resources/extensions/gsd/tests/triage-dispatch.test.ts +9 -9
- package/dist/resources/extensions/gsd/tests/verification-evidence.test.ts +743 -0
- package/dist/resources/extensions/gsd/tests/verification-gate.test.ts +965 -0
- package/dist/resources/extensions/gsd/tests/visualizer-data.test.ts +1 -1
- package/dist/resources/extensions/gsd/tests/visualizer-overlay.test.ts +44 -10
- package/dist/resources/extensions/gsd/tests/worktree.test.ts +3 -1
- package/dist/resources/extensions/gsd/types.ts +38 -0
- package/dist/resources/extensions/gsd/verification-evidence.ts +183 -0
- package/dist/resources/extensions/gsd/verification-gate.ts +567 -0
- package/dist/resources/extensions/gsd/visualizer-data.ts +25 -3
- package/dist/resources/extensions/gsd/visualizer-overlay.ts +31 -21
- package/dist/resources/extensions/gsd/visualizer-views.ts +15 -66
- package/dist/resources/extensions/search-the-web/tool-search.ts +26 -0
- package/dist/resources/extensions/shared/format-utils.ts +85 -0
- package/dist/resources/extensions/shared/tests/format-utils.test.ts +153 -0
- package/dist/resources/extensions/subagent/index.ts +46 -1
- package/dist/resources/extensions/subagent/isolation.ts +9 -6
- package/package.json +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.js +7 -4
- package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/packages/pi-ai/src/providers/openai-completions.ts +7 -4
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.js +7 -0
- package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.js +9 -2
- package/packages/pi-coding-agent/dist/core/lsp/config.js.map +1 -1
- package/packages/pi-coding-agent/src/core/lsp/client.ts +8 -0
- package/packages/pi-coding-agent/src/core/lsp/config.ts +9 -2
- package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
- package/packages/pi-tui/dist/components/editor.js +1 -1
- package/packages/pi-tui/dist/components/editor.js.map +1 -1
- package/packages/pi-tui/src/components/editor.ts +3 -1
- package/scripts/link-workspace-packages.cjs +22 -6
- package/src/resources/extensions/bg-shell/index.ts +19 -2
- package/src/resources/extensions/bg-shell/process-manager.ts +45 -0
- package/src/resources/extensions/bg-shell/types.ts +21 -1
- package/src/resources/extensions/gsd/auto/session.ts +224 -0
- package/src/resources/extensions/gsd/auto-budget.ts +32 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +63 -10
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +229 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +23 -10
- package/src/resources/extensions/gsd/auto-model-selection.ts +179 -0
- package/src/resources/extensions/gsd/auto-observability.ts +74 -0
- package/src/resources/extensions/gsd/auto-prompts.ts +0 -1
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +262 -0
- package/src/resources/extensions/gsd/auto-tool-tracking.ts +54 -0
- package/src/resources/extensions/gsd/auto-unit-closeout.ts +46 -0
- package/src/resources/extensions/gsd/auto-worktree-sync.ts +207 -0
- package/src/resources/extensions/gsd/auto.ts +977 -1551
- package/src/resources/extensions/gsd/commands.ts +3 -3
- package/src/resources/extensions/gsd/dashboard-overlay.ts +47 -72
- package/src/resources/extensions/gsd/doctor-proactive.ts +9 -4
- package/src/resources/extensions/gsd/export-html.ts +1001 -0
- package/src/resources/extensions/gsd/export.ts +49 -1
- package/src/resources/extensions/gsd/git-service.ts +6 -0
- package/src/resources/extensions/gsd/gitignore.ts +4 -1
- package/src/resources/extensions/gsd/guided-flow.ts +24 -5
- package/src/resources/extensions/gsd/index.ts +54 -1
- package/src/resources/extensions/gsd/native-git-bridge.ts +30 -2
- package/src/resources/extensions/gsd/observability-validator.ts +21 -0
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +231 -20
- package/src/resources/extensions/gsd/preferences.ts +62 -1
- package/src/resources/extensions/gsd/prompts/execute-task.md +4 -3
- package/src/resources/extensions/gsd/prompts/system.md +1 -1
- package/src/resources/extensions/gsd/reports.ts +510 -0
- package/src/resources/extensions/gsd/roadmap-slices.ts +1 -1
- package/src/resources/extensions/gsd/skills/gsd-headless/SKILL.md +178 -0
- package/src/resources/extensions/gsd/skills/gsd-headless/references/answer-injection.md +54 -0
- package/src/resources/extensions/gsd/skills/gsd-headless/references/commands.md +59 -0
- package/src/resources/extensions/gsd/skills/gsd-headless/references/multi-session.md +185 -0
- package/src/resources/extensions/gsd/state.ts +30 -0
- package/src/resources/extensions/gsd/templates/task-summary.md +9 -0
- package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +13 -0
- package/src/resources/extensions/gsd/tests/continue-here.test.ts +81 -0
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +5 -0
- package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/derive-state-draft.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +10 -1
- package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +132 -0
- package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/native-has-changes-cache.test.ts +61 -0
- package/src/resources/extensions/gsd/tests/network-error-fallback.test.ts +51 -1
- package/src/resources/extensions/gsd/tests/parallel-budget-atomicity.test.ts +331 -0
- package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +298 -0
- package/src/resources/extensions/gsd/tests/parallel-merge.test.ts +465 -0
- package/src/resources/extensions/gsd/tests/parallel-orchestration.test.ts +39 -10
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +71 -0
- package/src/resources/extensions/gsd/tests/replan-slice.test.ts +42 -0
- package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +9 -9
- package/src/resources/extensions/gsd/tests/verification-evidence.test.ts +743 -0
- package/src/resources/extensions/gsd/tests/verification-gate.test.ts +965 -0
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +44 -10
- package/src/resources/extensions/gsd/tests/worktree.test.ts +3 -1
- package/src/resources/extensions/gsd/types.ts +38 -0
- package/src/resources/extensions/gsd/verification-evidence.ts +183 -0
- package/src/resources/extensions/gsd/verification-gate.ts +567 -0
- package/src/resources/extensions/gsd/visualizer-data.ts +25 -3
- package/src/resources/extensions/gsd/visualizer-overlay.ts +31 -21
- package/src/resources/extensions/gsd/visualizer-views.ts +15 -66
- package/src/resources/extensions/search-the-web/tool-search.ts +26 -0
- package/src/resources/extensions/shared/format-utils.ts +85 -0
- package/src/resources/extensions/shared/tests/format-utils.test.ts +153 -0
- package/src/resources/extensions/subagent/index.ts +46 -1
- package/src/resources/extensions/subagent/isolation.ts +9 -6
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: gsd-headless
|
|
3
|
+
description: Orchestrate GSD (Get Shit Done) projects programmatically via headless CLI. Use when an agent needs to create milestones from specs, execute software development workflows, monitor task progress, check project status, or control GSD execution (pause/stop/skip/steer). Triggers on requests to "run gsd", "create milestone", "execute project", "check gsd status", "orchestrate development", "run headless workflow", or any programmatic interaction with the GSD project management system. Essential for building orchestrators that coordinate multiple GSD workers.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# GSD Headless Orchestration
|
|
7
|
+
|
|
8
|
+
Run GSD commands without TUI via `gsd headless`. Spawns an RPC child process, auto-responds to UI prompts, streams progress.
|
|
9
|
+
|
|
10
|
+
## Command Syntax
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
gsd headless [flags] [command] [args...]
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
**Flags:** `--timeout N` (ms, default 300000), `--json` (JSONL to stdout), `--model ID`, `--verbose`
|
|
17
|
+
**Exit codes:** 0=complete, 1=error/timeout, 2=blocked
|
|
18
|
+
|
|
19
|
+
## Core Workflows
|
|
20
|
+
|
|
21
|
+
### 1. Create + Execute a Milestone (end-to-end)
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
gsd headless new-milestone --context spec.md --auto
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Reads spec, bootstraps `.gsd/`, creates milestone, then chains into auto-mode executing all phases (discuss → research → plan → execute → summarize → complete).
|
|
28
|
+
|
|
29
|
+
Extra flags for `new-milestone`: `--context <path>` (use `-` for stdin), `--context-text <text>`, `--auto`.
|
|
30
|
+
|
|
31
|
+
### 2. Run All Queued Work
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
gsd headless auto
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Default command. Loops through all pending units until milestone complete or blocked.
|
|
38
|
+
|
|
39
|
+
### 3. Run One Unit
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
gsd headless next
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Execute exactly one unit (task/slice/milestone step), then exit. Ideal for step-by-step orchestration with external decision logic between steps.
|
|
46
|
+
|
|
47
|
+
### 4. Check Status
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
gsd headless --json status
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Returns project state: active milestone/slice/task, phase, progress counts, blockers. Parse the JSONL output for machine-readable state.
|
|
54
|
+
|
|
55
|
+
### 5. Dispatch Specific Phase
|
|
56
|
+
|
|
57
|
+
```bash
|
|
58
|
+
gsd headless dispatch research|plan|execute|complete|reassess|uat|replan
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Force-route to a specific phase, bypassing normal state-machine routing.
|
|
62
|
+
|
|
63
|
+
## Orchestrator Patterns
|
|
64
|
+
|
|
65
|
+
### Poll-and-React Loop
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# Check status, decide what to do
|
|
69
|
+
STATUS=$(gsd headless --json status 2>/dev/null)
|
|
70
|
+
EXIT=$?
|
|
71
|
+
|
|
72
|
+
case $EXIT in
|
|
73
|
+
0) echo "Complete" ;;
|
|
74
|
+
2) echo "Blocked — needs intervention" ;;
|
|
75
|
+
*) echo "Error" ;;
|
|
76
|
+
esac
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Step-by-Step with Monitoring
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
while true; do
|
|
83
|
+
gsd headless next
|
|
84
|
+
EXIT=$?
|
|
85
|
+
[ $EXIT -ne 0 ] && break
|
|
86
|
+
# Check progress, log, decide whether to continue
|
|
87
|
+
gsd headless --json status
|
|
88
|
+
done
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Multi-Session Orchestration
|
|
92
|
+
|
|
93
|
+
GSD tracks concurrent workers via file-based IPC in `.gsd/parallel/`. See [references/multi-session.md](references/multi-session.md) for the full architecture.
|
|
94
|
+
|
|
95
|
+
**Quick overview:**
|
|
96
|
+
|
|
97
|
+
Each worker spawns with `GSD_MILESTONE_LOCK=M00X` + its own git worktree. Workers write heartbeats to `.gsd/parallel/<milestoneId>.status.json`. The orchestrator enumerates all status files to get a dashboard of all workers, and sends commands via signal files.
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
# Spawn a worker for milestone M001 in its worktree
|
|
101
|
+
GSD_MILESTONE_LOCK=M001 GSD_PARALLEL_WORKER=1 \
|
|
102
|
+
gsd headless --json auto \
|
|
103
|
+
--cwd .gsd/worktrees/M001 2>worker-M001.log &
|
|
104
|
+
|
|
105
|
+
# Monitor all workers: read .gsd/parallel/*.status.json
|
|
106
|
+
for f in .gsd/parallel/*.status.json; do
|
|
107
|
+
jq '{mid: .milestoneId, state: .state, unit: .currentUnit.id, cost: .cost}' "$f"
|
|
108
|
+
done
|
|
109
|
+
|
|
110
|
+
# Send pause signal to M001
|
|
111
|
+
echo '{"signal":"pause","sentAt":'$(date +%s000)',"from":"coordinator"}' \
|
|
112
|
+
> .gsd/parallel/M001.signal.json
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
**Status file fields:** `milestoneId`, `pid`, `state` (running/paused/stopped/error), `currentUnit`, `completedUnits`, `cost`, `lastHeartbeat`, `startedAt`, `worktreePath`.
|
|
116
|
+
|
|
117
|
+
**Signal commands:** `pause`, `resume`, `stop`, `rebase`.
|
|
118
|
+
|
|
119
|
+
**Liveness detection:** PID alive check (`kill -0 $pid`) + heartbeat freshness (30s timeout). Stale sessions are auto-cleaned.
|
|
120
|
+
|
|
121
|
+
**For multiple projects:** each project has its own `.gsd/` directory. The orchestrator must track `(projectPath, milestoneId)` tuples externally.
|
|
122
|
+
|
|
123
|
+
### JSONL Event Stream
|
|
124
|
+
|
|
125
|
+
Use `--json` to get real-time events on stdout for downstream processing:
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
gsd headless --json auto 2>/dev/null | while read -r line; do
|
|
129
|
+
TYPE=$(echo "$line" | jq -r '.type')
|
|
130
|
+
case "$TYPE" in
|
|
131
|
+
tool_execution_start) echo "Tool: $(echo "$line" | jq -r '.toolName')" ;;
|
|
132
|
+
extension_ui_request) echo "GSD: $(echo "$line" | jq -r '.message // .title // empty')" ;;
|
|
133
|
+
agent_end) echo "Session ended" ;;
|
|
134
|
+
esac
|
|
135
|
+
done
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Event types: `agent_start`, `agent_end`, `tool_execution_start`, `tool_execution_end`, `extension_ui_request`, `message_update`, `error`.
|
|
139
|
+
|
|
140
|
+
## Answer Injection
|
|
141
|
+
|
|
142
|
+
Pre-supply answers for non-interactive runs. See [references/answer-injection.md](references/answer-injection.md) for schema and usage.
|
|
143
|
+
|
|
144
|
+
## GSD Project Structure
|
|
145
|
+
|
|
146
|
+
All state lives in `.gsd/` as markdown files (version-controllable):
|
|
147
|
+
|
|
148
|
+
```
|
|
149
|
+
.gsd/
|
|
150
|
+
milestones/M001/
|
|
151
|
+
M001-CONTEXT.md # Requirements, scope, decisions
|
|
152
|
+
M001-ROADMAP.md # Slices with tasks, dependencies, checkboxes
|
|
153
|
+
M001-SUMMARY.md # Completion summary
|
|
154
|
+
slices/S01/
|
|
155
|
+
S01-PLAN.md # Task list
|
|
156
|
+
S01-SUMMARY.md # Slice summary with frontmatter
|
|
157
|
+
tasks/T01-PLAN.md # Individual task spec
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
State is derived from files on disk — checkboxes in ROADMAP.md are the source of truth for completion.
|
|
161
|
+
|
|
162
|
+
## All Headless Commands
|
|
163
|
+
|
|
164
|
+
Quick reference — see [references/commands.md](references/commands.md) for the complete list.
|
|
165
|
+
|
|
166
|
+
| Command | Purpose |
|
|
167
|
+
|---------|---------|
|
|
168
|
+
| `auto` | Run all queued units (default) |
|
|
169
|
+
| `next` | Run one unit |
|
|
170
|
+
| `status` | Progress dashboard |
|
|
171
|
+
| `new-milestone` | Create milestone from spec |
|
|
172
|
+
| `queue` | Queue/reorder milestones |
|
|
173
|
+
| `history` | View execution history |
|
|
174
|
+
| `stop` / `pause` | Control auto-mode |
|
|
175
|
+
| `dispatch <phase>` | Force specific phase |
|
|
176
|
+
| `skip` / `undo` | Unit control |
|
|
177
|
+
| `doctor` | Health check + auto-fix |
|
|
178
|
+
| `steer <desc>` | Hard-steer plan mid-execution |
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Answer Injection
|
|
2
|
+
|
|
3
|
+
Pre-supply answers to eliminate interactive prompts during headless execution.
|
|
4
|
+
|
|
5
|
+
## Answer File Schema
|
|
6
|
+
|
|
7
|
+
```json
|
|
8
|
+
{
|
|
9
|
+
"questions": {
|
|
10
|
+
"question_id": "selected_option_label",
|
|
11
|
+
"multi_select_question": ["option_a", "option_b"]
|
|
12
|
+
},
|
|
13
|
+
"secrets": {
|
|
14
|
+
"API_KEY": "sk-...",
|
|
15
|
+
"DATABASE_URL": "postgres://..."
|
|
16
|
+
},
|
|
17
|
+
"defaults": {
|
|
18
|
+
"strategy": "first_option"
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Fields
|
|
24
|
+
|
|
25
|
+
- **questions**: Map question ID → answer. String for single-select, string[] for multi-select.
|
|
26
|
+
- **secrets**: Map env var name → value. Used for `secure_env_collect` tool calls. Values are never logged.
|
|
27
|
+
- **defaults.strategy**: Fallback for unmatched questions.
|
|
28
|
+
- `"first_option"` — auto-select first available option
|
|
29
|
+
- `"cancel"` — cancel the request
|
|
30
|
+
|
|
31
|
+
## How It Works
|
|
32
|
+
|
|
33
|
+
Two-phase correlation:
|
|
34
|
+
1. **Observe** `tool_execution_start` events for `ask_user_questions` — extracts question metadata (ID, options, allowMultiple)
|
|
35
|
+
2. **Match** subsequent `extension_ui_request` events to metadata, respond with pre-supplied answer
|
|
36
|
+
|
|
37
|
+
Handles out-of-order events (extension_ui_request can arrive before tool_execution_start in RPC mode) via deferred processing queue.
|
|
38
|
+
|
|
39
|
+
## Without Answer Injection
|
|
40
|
+
|
|
41
|
+
Headless mode has built-in auto-responders:
|
|
42
|
+
- **select** → picks first option
|
|
43
|
+
- **confirm** → auto-confirms
|
|
44
|
+
- **input** → empty string
|
|
45
|
+
- **editor** → returns prefill or empty
|
|
46
|
+
|
|
47
|
+
Answer injection overrides these defaults with specific answers when precision matters.
|
|
48
|
+
|
|
49
|
+
## Diagnostics
|
|
50
|
+
|
|
51
|
+
The injector tracks stats:
|
|
52
|
+
- `questionsAnswered` / `questionsDefaulted`
|
|
53
|
+
- `secretsProvided` / `secretsMissing`
|
|
54
|
+
- `fireAndForgetConsumed` / `confirmationsHandled`
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# GSD Commands Reference
|
|
2
|
+
|
|
3
|
+
All commands can be run via `gsd headless [command]`.
|
|
4
|
+
|
|
5
|
+
## Workflow Commands
|
|
6
|
+
|
|
7
|
+
| Command | Description |
|
|
8
|
+
|---------|-------------|
|
|
9
|
+
| `auto` | Autonomous mode — loop until milestone complete (default) |
|
|
10
|
+
| `next` | Step mode — execute one unit, then exit |
|
|
11
|
+
| `stop` | Stop auto-mode gracefully |
|
|
12
|
+
| `pause` | Pause auto-mode (preserves state, resumable) |
|
|
13
|
+
| `new-milestone` | Create milestone from specification (requires `--context`) |
|
|
14
|
+
| `dispatch <phase>` | Force-dispatch: research, plan, execute, complete, reassess, uat, replan |
|
|
15
|
+
|
|
16
|
+
## Status & Monitoring
|
|
17
|
+
|
|
18
|
+
| Command | Description |
|
|
19
|
+
|---------|-------------|
|
|
20
|
+
| `status` | Progress dashboard (active unit, phase, blockers) |
|
|
21
|
+
| `visualize` | Workflow visualizer (deps, metrics, timeline) |
|
|
22
|
+
| `history` | Execution history (supports --cost, --phase, --model, limit) |
|
|
23
|
+
|
|
24
|
+
## Unit Control
|
|
25
|
+
|
|
26
|
+
| Command | Description |
|
|
27
|
+
|---------|-------------|
|
|
28
|
+
| `skip` | Prevent a unit from auto-mode dispatch |
|
|
29
|
+
| `undo` | Revert last completed unit (--force flag) |
|
|
30
|
+
| `steer <desc>` | Hard-steer plan documents during execution |
|
|
31
|
+
| `queue` | Queue and reorder future milestones |
|
|
32
|
+
| `capture` | Fire-and-forget thought capture |
|
|
33
|
+
| `triage` | Manually trigger triage of pending captures |
|
|
34
|
+
|
|
35
|
+
## Configuration & Health
|
|
36
|
+
|
|
37
|
+
| Command | Description |
|
|
38
|
+
|---------|-------------|
|
|
39
|
+
| `prefs` | Manage preferences (global/project/status/wizard/setup) |
|
|
40
|
+
| `config` | Set API keys for external tools |
|
|
41
|
+
| `doctor` | Runtime health checks with auto-fix |
|
|
42
|
+
| `hooks` | Show configured post-unit and pre-dispatch hooks |
|
|
43
|
+
| `knowledge <rule\|pattern\|lesson>` | Add persistent project knowledge |
|
|
44
|
+
| `cleanup` | Remove merged branches or snapshots |
|
|
45
|
+
| `export` | Export results (--json, --markdown) |
|
|
46
|
+
| `migrate` | Migrate v1 .planning directory to .gsd format |
|
|
47
|
+
|
|
48
|
+
## Phases
|
|
49
|
+
|
|
50
|
+
GSD workflows progress through these phases:
|
|
51
|
+
`pre-planning` → `needs-discussion` → `discussing` → `researching` → `planning` → `executing` → `verifying` → `summarizing` → `advancing` → `validating-milestone` → `completing-milestone` → `complete`
|
|
52
|
+
|
|
53
|
+
Special phases: `paused`, `blocked`, `replanning-slice`
|
|
54
|
+
|
|
55
|
+
## Hierarchy
|
|
56
|
+
|
|
57
|
+
- **Milestone**: Shippable version (4-10 slices, 1-4 weeks)
|
|
58
|
+
- **Slice**: One demoable vertical capability (1-7 tasks, 1-3 days)
|
|
59
|
+
- **Task**: One context-window-sized unit of work (one session)
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
# Multi-Session Orchestration
|
|
2
|
+
|
|
3
|
+
How to run and monitor multiple concurrent GSD sessions.
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
GSD uses **file-based IPC** — no sockets or ports. All coordination happens through JSON files in `.gsd/parallel/`.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
.gsd/parallel/
|
|
11
|
+
├── M001.status.json # Worker heartbeat + state
|
|
12
|
+
├── M001.signal.json # Coordinator → worker commands (ephemeral)
|
|
13
|
+
├── M002.status.json
|
|
14
|
+
├── M003.status.json
|
|
15
|
+
└── ...
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Worker Isolation
|
|
19
|
+
|
|
20
|
+
Each worker gets:
|
|
21
|
+
1. **`GSD_MILESTONE_LOCK=M00X`** — state derivation only sees this milestone
|
|
22
|
+
2. **`GSD_PARALLEL_WORKER=1`** — prevents nested parallel spawns
|
|
23
|
+
3. **Own git worktree** at `.gsd/worktrees/M00X/` — branch `milestone/M00X`
|
|
24
|
+
|
|
25
|
+
Workers cannot interfere with each other. Each has its own filesystem and git branch.
|
|
26
|
+
|
|
27
|
+
## Status File Schema
|
|
28
|
+
|
|
29
|
+
Written atomically (`.tmp` + rename) by each worker at `.gsd/parallel/<milestoneId>.status.json`:
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"milestoneId": "M001",
|
|
34
|
+
"pid": 12345,
|
|
35
|
+
"state": "running",
|
|
36
|
+
"currentUnit": {
|
|
37
|
+
"type": "task",
|
|
38
|
+
"id": "T03",
|
|
39
|
+
"startedAt": 1710000000000
|
|
40
|
+
},
|
|
41
|
+
"completedUnits": 7,
|
|
42
|
+
"cost": 1.23,
|
|
43
|
+
"lastHeartbeat": 1710000015000,
|
|
44
|
+
"startedAt": 1710000000000,
|
|
45
|
+
"worktreePath": ".gsd/worktrees/M001"
|
|
46
|
+
}
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
**States:** `running`, `paused`, `stopped`, `error`
|
|
50
|
+
|
|
51
|
+
## Signal Files
|
|
52
|
+
|
|
53
|
+
Coordinator writes to `.gsd/parallel/<milestoneId>.signal.json`. Worker consumes and deletes on next dispatch cycle.
|
|
54
|
+
|
|
55
|
+
```json
|
|
56
|
+
{
|
|
57
|
+
"signal": "pause",
|
|
58
|
+
"sentAt": 1710000020000,
|
|
59
|
+
"from": "coordinator"
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**Signals:** `pause`, `resume`, `stop`, `rebase`
|
|
64
|
+
|
|
65
|
+
## Spawning Workers
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
# Spawn worker in its worktree
|
|
69
|
+
GSD_MILESTONE_LOCK=M001 \
|
|
70
|
+
GSD_PARALLEL_WORKER=1 \
|
|
71
|
+
GSD_BIN_PATH=$(which gsd) \
|
|
72
|
+
gsd --mode json --print "/gsd auto" \
|
|
73
|
+
2>logs/M001.log &
|
|
74
|
+
WORKER_PID=$!
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
Workers emit NDJSON on stdout. Parse `message_end` events for cost tracking:
|
|
78
|
+
```bash
|
|
79
|
+
# Extract cost from worker output
|
|
80
|
+
gsd --mode json --print "/gsd auto" | while read -r line; do
|
|
81
|
+
COST=$(echo "$line" | jq -r 'select(.type=="message_end") | .message.usage.cost.total // empty')
|
|
82
|
+
[ -n "$COST" ] && echo "Cost update: $COST"
|
|
83
|
+
done
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Monitoring All Workers
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
# Dashboard: enumerate all status files
|
|
90
|
+
for f in .gsd/parallel/*.status.json; do
|
|
91
|
+
[ -f "$f" ] || continue
|
|
92
|
+
jq -r '[.milestoneId, .state, (.currentUnit.id // "idle"), "\(.cost | tostring)$"] | join("\t")' "$f"
|
|
93
|
+
done
|
|
94
|
+
|
|
95
|
+
# Liveness check
|
|
96
|
+
for f in .gsd/parallel/*.status.json; do
|
|
97
|
+
PID=$(jq -r '.pid' "$f")
|
|
98
|
+
MID=$(jq -r '.milestoneId' "$f")
|
|
99
|
+
if kill -0 "$PID" 2>/dev/null; then
|
|
100
|
+
echo "$MID: alive (pid=$PID)"
|
|
101
|
+
else
|
|
102
|
+
echo "$MID: DEAD (pid=$PID) — cleanup needed"
|
|
103
|
+
rm "$f"
|
|
104
|
+
fi
|
|
105
|
+
done
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Sending Commands
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
# Pause a worker
|
|
112
|
+
send_signal() {
|
|
113
|
+
local MID=$1 SIGNAL=$2
|
|
114
|
+
echo "{\"signal\":\"$SIGNAL\",\"sentAt\":$(date +%s000),\"from\":\"coordinator\"}" \
|
|
115
|
+
> ".gsd/parallel/${MID}.signal.json"
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
send_signal M001 pause
|
|
119
|
+
send_signal M002 stop
|
|
120
|
+
send_signal M003 resume
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Budget Enforcement
|
|
124
|
+
|
|
125
|
+
Track aggregate cost across all workers:
|
|
126
|
+
```bash
|
|
127
|
+
TOTAL=$(jq -s 'map(.cost) | add // 0' .gsd/parallel/*.status.json)
|
|
128
|
+
CEILING=50.00
|
|
129
|
+
if (( $(echo "$TOTAL > $CEILING" | bc -l) )); then
|
|
130
|
+
echo "Budget exceeded ($TOTAL > $CEILING) — stopping all"
|
|
131
|
+
for f in .gsd/parallel/*.status.json; do
|
|
132
|
+
MID=$(jq -r '.milestoneId' "$f")
|
|
133
|
+
send_signal "$MID" stop
|
|
134
|
+
done
|
|
135
|
+
fi
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
## Stale Session Cleanup
|
|
139
|
+
|
|
140
|
+
A session is stale when:
|
|
141
|
+
- PID is dead (`kill -0 $pid` fails), OR
|
|
142
|
+
- `lastHeartbeat` is older than 30 seconds
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
NOW=$(date +%s000)
|
|
146
|
+
STALE_THRESHOLD=30000
|
|
147
|
+
for f in .gsd/parallel/*.status.json; do
|
|
148
|
+
PID=$(jq -r '.pid' "$f")
|
|
149
|
+
HB=$(jq -r '.lastHeartbeat' "$f")
|
|
150
|
+
AGE=$((NOW - HB))
|
|
151
|
+
if ! kill -0 "$PID" 2>/dev/null || [ "$AGE" -gt "$STALE_THRESHOLD" ]; then
|
|
152
|
+
echo "Stale: $(jq -r '.milestoneId' "$f") — removing"
|
|
153
|
+
rm "$f"
|
|
154
|
+
fi
|
|
155
|
+
done
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Multi-Project Orchestration
|
|
159
|
+
|
|
160
|
+
Within one project, milestones are tracked automatically in `.gsd/parallel/`. For orchestrating across **multiple projects**, maintain an external registry:
|
|
161
|
+
|
|
162
|
+
```json
|
|
163
|
+
{
|
|
164
|
+
"sessions": [
|
|
165
|
+
{ "project": "/path/to/project-a", "milestoneId": "M001" },
|
|
166
|
+
{ "project": "/path/to/project-b", "milestoneId": "M001" },
|
|
167
|
+
{ "project": "/path/to/project-b", "milestoneId": "M002" }
|
|
168
|
+
]
|
|
169
|
+
}
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Then poll each project's `.gsd/parallel/` directory. GSD has no cross-project awareness — the orchestrator must bridge this gap.
|
|
173
|
+
|
|
174
|
+
## Built-in Parallel Commands
|
|
175
|
+
|
|
176
|
+
Inside an interactive GSD session, these commands manage the parallel orchestrator:
|
|
177
|
+
|
|
178
|
+
| Command | Description |
|
|
179
|
+
|---------|-------------|
|
|
180
|
+
| `/gsd parallel start` | Analyze eligibility, spawn workers |
|
|
181
|
+
| `/gsd parallel status` | Show all workers, costs, progress |
|
|
182
|
+
| `/gsd parallel stop [MID]` | Stop one or all workers |
|
|
183
|
+
| `/gsd parallel pause [MID]` | Pause without killing |
|
|
184
|
+
| `/gsd parallel resume [MID]` | Resume paused worker |
|
|
185
|
+
| `/gsd parallel merge [MID]` | Merge completed milestone branch |
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
resolveSlicePath,
|
|
27
27
|
resolveSliceFile,
|
|
28
28
|
resolveTaskFile,
|
|
29
|
+
resolveTasksDir,
|
|
29
30
|
resolveGsdRootFile,
|
|
30
31
|
gsdRoot,
|
|
31
32
|
} from './paths.js';
|
|
@@ -34,6 +35,7 @@ import { milestoneIdSort, findMilestoneIds } from './guided-flow.js';
|
|
|
34
35
|
import { nativeBatchParseGsdFiles, type BatchParsedFile } from './native-parser-bridge.js';
|
|
35
36
|
|
|
36
37
|
import { join, resolve } from 'path';
|
|
38
|
+
import { existsSync, readdirSync } from 'node:fs';
|
|
37
39
|
import { debugCount, debugTime } from './debug-logger.js';
|
|
38
40
|
|
|
39
41
|
// ─── Query Functions ───────────────────────────────────────────────────────
|
|
@@ -573,6 +575,34 @@ async function _deriveStateImpl(basePath: string): Promise<GSDState> {
|
|
|
573
575
|
title: activeTaskEntry.title,
|
|
574
576
|
};
|
|
575
577
|
|
|
578
|
+
// ── Task plan file check (#909) ──────────────────────────────────────
|
|
579
|
+
// The slice plan may reference tasks but per-task plan files may be
|
|
580
|
+
// missing — e.g. when the slice plan was pre-created during roadmapping.
|
|
581
|
+
// If the tasks dir exists but has literally zero files (empty dir from
|
|
582
|
+
// mkdir), fall back to planning so plan-slice generates task plans.
|
|
583
|
+
const tasksDir = resolveTasksDir(basePath, activeMilestone.id, activeSlice.id);
|
|
584
|
+
if (tasksDir && existsSync(tasksDir) && slicePlan.tasks.length > 0) {
|
|
585
|
+
const allFiles = readdirSync(tasksDir).filter(f => f.endsWith(".md"));
|
|
586
|
+
if (allFiles.length === 0) {
|
|
587
|
+
return {
|
|
588
|
+
activeMilestone,
|
|
589
|
+
activeSlice,
|
|
590
|
+
activeTask: null,
|
|
591
|
+
phase: 'planning',
|
|
592
|
+
recentDecisions: [],
|
|
593
|
+
blockers: [],
|
|
594
|
+
nextAction: `Task plan files missing for ${activeSlice.id}. Run plan-slice to generate task plans.`,
|
|
595
|
+
registry,
|
|
596
|
+
requirements,
|
|
597
|
+
progress: {
|
|
598
|
+
milestones: milestoneProgress,
|
|
599
|
+
slices: sliceProgress,
|
|
600
|
+
tasks: taskProgress,
|
|
601
|
+
},
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
576
606
|
// ── Blocker detection: scan completed task summaries ──────────────────
|
|
577
607
|
// If any completed task has blocker_discovered: true and no REPLAN.md
|
|
578
608
|
// exists yet, transition to replanning-slice instead of executing.
|
|
@@ -37,6 +37,15 @@ blocker_discovered: false
|
|
|
37
37
|
|
|
38
38
|
{{whatWasVerifiedAndHow — commands run, tests passed, behavior confirmed}}
|
|
39
39
|
|
|
40
|
+
## Verification Evidence
|
|
41
|
+
|
|
42
|
+
<!-- Populated from verification gate output. If the gate ran, fill in the table below.
|
|
43
|
+
If no gate ran (e.g., no verification commands discovered), note that. -->
|
|
44
|
+
|
|
45
|
+
| # | Command | Exit Code | Verdict | Duration |
|
|
46
|
+
|---|---------|-----------|---------|----------|
|
|
47
|
+
| {{row}} | {{command}} | {{exitCode}} | {{verdict}} | {{duration}} |
|
|
48
|
+
|
|
40
49
|
## Diagnostics
|
|
41
50
|
|
|
42
51
|
{{howToInspectWhatThisTaskBuiltLater — status surfaces, logs, error shapes, failure artifacts, or none}}
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
describeNextUnit,
|
|
8
8
|
formatAutoElapsed,
|
|
9
9
|
formatWidgetTokens,
|
|
10
|
+
estimateTimeRemaining,
|
|
10
11
|
} from "../auto-dashboard.ts";
|
|
11
12
|
|
|
12
13
|
// ─── unitVerb ─────────────────────────────────────────────────────────────
|
|
@@ -151,3 +152,15 @@ test("formatWidgetTokens formats millions with M", () => {
|
|
|
151
152
|
assert.equal(formatWidgetTokens(10_000_000), "10M");
|
|
152
153
|
assert.equal(formatWidgetTokens(25_000_000), "25M");
|
|
153
154
|
});
|
|
155
|
+
|
|
156
|
+
// ─── estimateTimeRemaining ──────────────────────────────────────────────
|
|
157
|
+
|
|
158
|
+
test("estimateTimeRemaining returns null when no ledger data", () => {
|
|
159
|
+
// With no active auto-mode session, ledger is empty
|
|
160
|
+
const result = estimateTimeRemaining();
|
|
161
|
+
assert.equal(result, null);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
test("estimateTimeRemaining is exported and callable", () => {
|
|
165
|
+
assert.equal(typeof estimateTimeRemaining, "function");
|
|
166
|
+
});
|
|
@@ -201,4 +201,85 @@ describe("continue-here", () => {
|
|
|
201
201
|
}
|
|
202
202
|
});
|
|
203
203
|
});
|
|
204
|
+
|
|
205
|
+
describe("context-pressure monitor integration", () => {
|
|
206
|
+
it("should fire wrap-up when context >= threshold and mark continueHereFired", async () => {
|
|
207
|
+
const { writeUnitRuntimeRecord, readUnitRuntimeRecord, clearUnitRuntimeRecord } = await import("../unit-runtime.js");
|
|
208
|
+
const fs = await import("node:fs");
|
|
209
|
+
const path = await import("node:path");
|
|
210
|
+
const os = await import("node:os");
|
|
211
|
+
|
|
212
|
+
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "continue-here-monitor-"));
|
|
213
|
+
try {
|
|
214
|
+
// Simulate the monitor's one-shot logic:
|
|
215
|
+
// 1. Write initial runtime record (continueHereFired=false)
|
|
216
|
+
const startedAt = Date.now();
|
|
217
|
+
writeUnitRuntimeRecord(tmpDir, "execute-task", "M001/S01/T01", startedAt, {
|
|
218
|
+
phase: "dispatched",
|
|
219
|
+
wrapupWarningSent: false,
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
const budget = computeBudgets(128_000);
|
|
223
|
+
const threshold = budget.continueThresholdPercent;
|
|
224
|
+
|
|
225
|
+
// Simulate the monitor poll: context at 75% (above threshold)
|
|
226
|
+
const contextPercent = 75;
|
|
227
|
+
const runtime = readUnitRuntimeRecord(tmpDir, "execute-task", "M001/S01/T01");
|
|
228
|
+
assert.ok(runtime, "runtime record should exist");
|
|
229
|
+
assert.equal(runtime!.continueHereFired, false, "initially false");
|
|
230
|
+
|
|
231
|
+
// Check: should fire
|
|
232
|
+
const shouldFire = !runtime!.continueHereFired
|
|
233
|
+
&& contextPercent >= threshold;
|
|
234
|
+
assert.ok(shouldFire, "should fire when context >= threshold and not yet fired");
|
|
235
|
+
|
|
236
|
+
// Mark as fired (what the monitor does)
|
|
237
|
+
writeUnitRuntimeRecord(tmpDir, "execute-task", "M001/S01/T01", startedAt, {
|
|
238
|
+
continueHereFired: true,
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// Verify one-shot: second poll should NOT fire
|
|
242
|
+
const runtime2 = readUnitRuntimeRecord(tmpDir, "execute-task", "M001/S01/T01");
|
|
243
|
+
assert.ok(runtime2, "runtime record should still exist");
|
|
244
|
+
assert.equal(runtime2!.continueHereFired, true, "should be marked as fired");
|
|
245
|
+
|
|
246
|
+
const shouldFireAgain = !runtime2!.continueHereFired
|
|
247
|
+
&& contextPercent >= threshold;
|
|
248
|
+
assert.equal(shouldFireAgain, false, "must not fire again — one-shot guard");
|
|
249
|
+
|
|
250
|
+
// Clean up
|
|
251
|
+
clearUnitRuntimeRecord(tmpDir, "execute-task", "M001/S01/T01");
|
|
252
|
+
} finally {
|
|
253
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it("should not fire when context is below threshold", () => {
|
|
258
|
+
const budget = computeBudgets(200_000);
|
|
259
|
+
const threshold = budget.continueThresholdPercent;
|
|
260
|
+
|
|
261
|
+
// Simulate monitor poll with context at 50%
|
|
262
|
+
const contextPercent = 50;
|
|
263
|
+
const continueHereFired = false;
|
|
264
|
+
const shouldFire = !continueHereFired && contextPercent >= threshold;
|
|
265
|
+
assert.equal(shouldFire, false, "50% should not trigger continue-here");
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it("should not fire when contextUsage is null/undefined", () => {
|
|
269
|
+
const budget = computeBudgets(128_000);
|
|
270
|
+
const threshold = budget.continueThresholdPercent;
|
|
271
|
+
|
|
272
|
+
// Simulate the full guard chain from the monitor
|
|
273
|
+
const usageUndefined = undefined as { percent: number | null } | undefined;
|
|
274
|
+
const shouldFire1 = usageUndefined != null
|
|
275
|
+
&& usageUndefined.percent != null
|
|
276
|
+
&& usageUndefined.percent >= threshold;
|
|
277
|
+
assert.equal(shouldFire1, false, "undefined usage must not fire");
|
|
278
|
+
|
|
279
|
+
const usageNullPercent: { percent: number | null } = { percent: null };
|
|
280
|
+
const shouldFire2 = usageNullPercent.percent != null
|
|
281
|
+
&& usageNullPercent.percent >= threshold;
|
|
282
|
+
assert.equal(shouldFire2, false, "null percent must not fire");
|
|
283
|
+
});
|
|
284
|
+
});
|
|
204
285
|
});
|