@tekyzinc/gsd-t 2.74.13 → 3.10.10

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.
Files changed (69) hide show
  1. package/CHANGELOG.md +165 -0
  2. package/README.md +117 -1
  3. package/bin/advisor-integration.js +93 -0
  4. package/bin/check-headless-sessions.js +140 -0
  5. package/bin/context-meter-config.cjs +101 -0
  6. package/bin/context-meter-config.test.cjs +101 -0
  7. package/bin/gsd-t-unattended-platform.js +381 -0
  8. package/bin/gsd-t-unattended-safety.js +766 -0
  9. package/bin/gsd-t-unattended.js +1259 -0
  10. package/bin/gsd-t.js +723 -19
  11. package/bin/handoff-lock.js +249 -0
  12. package/bin/headless-auto-spawn.js +328 -0
  13. package/bin/model-selector.js +224 -0
  14. package/bin/runway-estimator.js +242 -0
  15. package/bin/token-budget.js +96 -89
  16. package/bin/token-optimizer.js +471 -0
  17. package/bin/token-telemetry.js +246 -0
  18. package/commands/gsd-t-audit.md +3 -3
  19. package/commands/gsd-t-backlog-list.md +38 -0
  20. package/commands/gsd-t-brainstorm.md +3 -3
  21. package/commands/gsd-t-complete-milestone.md +24 -0
  22. package/commands/gsd-t-debug.md +124 -7
  23. package/commands/gsd-t-discuss.md +10 -3
  24. package/commands/gsd-t-doc-ripple.md +32 -4
  25. package/commands/gsd-t-execute.md +107 -52
  26. package/commands/gsd-t-help.md +22 -0
  27. package/commands/gsd-t-integrate.md +67 -4
  28. package/commands/gsd-t-optimization-apply.md +91 -0
  29. package/commands/gsd-t-optimization-reject.md +94 -0
  30. package/commands/gsd-t-partition.md +7 -0
  31. package/commands/gsd-t-pause.md +3 -0
  32. package/commands/gsd-t-plan.md +10 -3
  33. package/commands/gsd-t-prd.md +3 -3
  34. package/commands/gsd-t-quick.md +71 -9
  35. package/commands/gsd-t-reflect.md +3 -7
  36. package/commands/gsd-t-resume.md +86 -1
  37. package/commands/gsd-t-status.md +31 -0
  38. package/commands/gsd-t-test-sync.md +7 -0
  39. package/commands/gsd-t-unattended-stop.md +83 -0
  40. package/commands/gsd-t-unattended-watch.md +290 -0
  41. package/commands/gsd-t-unattended.md +414 -0
  42. package/commands/gsd-t-verify.md +12 -5
  43. package/commands/gsd-t-visualize.md +3 -7
  44. package/commands/gsd-t-wave.md +82 -18
  45. package/docs/GSD-T-README.md +69 -0
  46. package/docs/architecture.md +176 -4
  47. package/docs/infrastructure.md +221 -0
  48. package/docs/methodology.md +44 -0
  49. package/docs/prd-harness-evolution.md +51 -37
  50. package/docs/requirements.md +95 -0
  51. package/docs/unattended-windows-caveats.md +245 -0
  52. package/package.json +2 -2
  53. package/scripts/context-meter/count-tokens-client.js +221 -0
  54. package/scripts/context-meter/count-tokens-client.test.js +308 -0
  55. package/scripts/context-meter/test-injector.js +55 -0
  56. package/scripts/context-meter/threshold.js +88 -0
  57. package/scripts/context-meter/threshold.test.js +255 -0
  58. package/scripts/context-meter/transcript-parser.js +252 -0
  59. package/scripts/context-meter/transcript-parser.test.js +320 -0
  60. package/scripts/gsd-t-context-meter.e2e.test.js +415 -0
  61. package/scripts/gsd-t-context-meter.js +350 -0
  62. package/scripts/gsd-t-context-meter.test.js +417 -0
  63. package/scripts/gsd-t-heartbeat.js +2 -2
  64. package/scripts/gsd-t-statusline.js +23 -8
  65. package/templates/CLAUDE-global.md +17 -1
  66. package/templates/CLAUDE-project.md +26 -6
  67. package/templates/context-meter-config.json +10 -0
  68. package/templates/prompts/README.md +1 -1
  69. package/bin/task-counter.cjs +0 -161
@@ -2,6 +2,13 @@
2
2
 
3
3
  You are the lead agent coordinating verification of the completed work. Each verification dimension should be thorough and independent.
4
4
 
5
+ ## Model Assignment
6
+
7
+ Per `.gsd-t/contracts/model-selection-contract.md` v1.0.0.
8
+
9
+ - **Default**: `opus` (`selectModel({phase: "verify"})`) — milestone verification is the final quality gate before completion. High stakes.
10
+ - **Escalation**: already at opus; there is no stronger tier. Verify judgments are always made at full quality.
11
+
5
12
  ## Step 1: Load State
6
13
 
7
14
  Read:
@@ -161,9 +168,9 @@ Before spawning — run via Bash:
161
168
  `T_START=$(date +%s) && DT_START=$(date +"%Y-%m-%d %H:%M")`
162
169
  Spawn a Task subagent to run the full test suite and contract audit.
163
170
  After subagent returns — run via Bash:
164
- `T_END=$(date +%s) && DT_END=$(date +"%Y-%m-%d %H:%M") && DURATION=$((T_END-T_START))`
165
- Append to `.gsd-t/token-log.md` (create with header `| Datetime-start | Datetime-end | Command | Step | Model | Duration(s) | Notes | Tasks-Since-Reset |` if missing):
166
- `| {DT_START} | {DT_END} | gsd-t-verify | Step 4 | haiku | {DURATION}s | test audit + contract review | {COUNTER} |`
171
+ `T_END=$(date +%s) && DT_END=$(date +"%Y-%m-%d %H:%M") && DURATION=$((T_END-T_START)) && CTX_PCT=$(node -e "const tb=require('./bin/token-budget.js'); process.stdout.write(String(tb.getSessionStatus('.').pct||'N/A'))" 2>/dev/null || echo "N/A")`
172
+ Append to `.gsd-t/token-log.md` (create with header `| Datetime-start | Datetime-end | Command | Step | Model | Duration(s) | Notes | Ctx% |` if missing):
173
+ `| {DT_START} | {DT_END} | gsd-t-verify | Step 4 | haiku | {DURATION}s | test audit + contract review | {CTX_PCT} |`
167
174
  Collect all reports, synthesize, create remediation plan.
168
175
  ```
169
176
 
@@ -367,9 +374,9 @@ Report back: one-line status summary."
367
374
  ```
368
375
 
369
376
  After subagent returns — run via Bash:
370
- `T_END=$(date +%s) && DT_END=$(date +"%Y-%m-%d %H:%M") && DURATION=$((T_END-T_START))`
377
+ `T_END=$(date +%s) && DT_END=$(date +"%Y-%m-%d %H:%M") && DURATION=$((T_END-T_START)) && CTX_PCT=$(node -e "const tb=require('./bin/token-budget.js'); process.stdout.write(String(tb.getSessionStatus('.').pct||'N/A'))" 2>/dev/null || echo "N/A")`
371
378
  Append to `.gsd-t/token-log.md`:
372
- `| {DT_START} | {DT_END} | gsd-t-verify | Step 8 | sonnet | {DURATION}s | auto-complete-milestone | | | {COUNTER} |`
379
+ `| {DT_START} | {DT_END} | gsd-t-verify | Step 8 | sonnet | {DURATION}s | auto-complete-milestone | | | {CTX_PCT} |`
373
380
 
374
381
  3. Verify subagent result: Read `.gsd-t/progress.md` — confirm status is COMPLETED. If not, report the failure.
375
382
 
@@ -21,14 +21,10 @@ Skip Step 0 — you are already the subagent."
21
21
  **OBSERVABILITY LOGGING — after subagent returns:**
22
22
 
23
23
  Run via Bash:
24
- `T_END=$(date +%s) && DT_END=$(date +"%Y-%m-%d %H:%M") && DURATION=$((T_END-T_START))`
24
+ `T_END=$(date +%s) && DT_END=$(date +"%Y-%m-%d %H:%M") && DURATION=$((T_END-T_START)) && CTX_PCT=$(node -e "const tb=require('./bin/token-budget.js'); process.stdout.write(String(tb.getSessionStatus('.').pct||'N/A'))" 2>/dev/null || echo "N/A")`
25
25
 
26
- Compute tokens:
27
- - No compaction (TOK_END >= TOK_START): `TOKENS=$((TOK_END-TOK_START))`, COMPACTED=null
28
- - Compaction detected (TOK_END < TOK_START): `TOKENS=$(((TOK_MAX-TOK_START)+TOK_END))`, COMPACTED=$DT_END
29
-
30
- Append to `.gsd-t/token-log.md` (create with header `| Datetime-start | Datetime-end | Command | Step | Model | Duration(s) | Notes | Tasks-Since-Reset |` if missing):
31
- `| {DT_START} | {DT_END} | gsd-t-visualize | Step 0 | sonnet | {DURATION}s | dashboard launched | {COUNTER} |`
26
+ Append to `.gsd-t/token-log.md` (create with header `| Datetime-start | Datetime-end | Command | Step | Model | Duration(s) | Notes | Ctx% |` if missing):
27
+ `| {DT_START} | {DT_END} | gsd-t-visualize | Step 0 | sonnet | {DURATION}s | dashboard launched | {CTX_PCT} |`
32
28
 
33
29
  Return the subagent's output and stop. Only skip Step 0 if you are already running as a subagent.
34
30
 
@@ -2,15 +2,77 @@
2
2
 
3
3
  You are the wave orchestrator. You do NOT execute phases yourself. Instead, you spawn an **independent agent for each phase**, giving each a fresh context window. This eliminates context accumulation across phases and prevents mid-wave compaction.
4
4
 
5
- ## Step 0: Reset Phase-Count Gate (MANDATORY — first thing in a fresh session)
5
+ ## Model Assignment
6
+
7
+ Per `.gsd-t/contracts/model-selection-contract.md` v1.0.0. Each phase spawn picks its tier from `bin/model-selector.js` — the wave orchestrator itself is routine coordination work.
8
+
9
+ - **Wave orchestrator (this agent)**: `sonnet` (`selectModel({phase: "wave"})`).
10
+ - **Per-phase spawns**: each phase agent selects its own tier — `partition` → opus, `discuss` → opus, `plan` → sonnet, `execute` → sonnet (with mechanical haiku subroutines and opus Red Team), `test-sync` → sonnet, `integrate` → sonnet, `verify` → opus, `doc-ripple` → sonnet. The phase command files carry their own Model Assignment blocks.
11
+ - **Escalation**: `/advisor` convention-based fallback from `bin/advisor-integration.js` at declared high-stakes sub-decisions (see `.gsd-t/M35-advisor-findings.md`). Never silently downgrade the model or skip phases under context pressure — M35 removed that behavior.
12
+
13
+ ## Per-Spawn Token Bracket (MANDATORY — wrap EVERY phase agent spawn)
14
+
15
+ Per `.gsd-t/contracts/token-telemetry-contract.md` v1.0.0. Every phase agent spawn in the Phase Agent Spawn Pattern below **MUST** be wrapped in this token bracket so `.gsd-t/token-metrics.jsonl` gets one record per spawn. This is additive — the existing OBSERVABILITY LOGGING blocks in each spawn site are preserved unmodified alongside this bracket.
16
+
17
+ **Before each phase spawn — read starting context tokens:**
18
+
19
+ ```bash
20
+ T0_TOKENS=$(node -e "try{const s=require('fs').readFileSync('.gsd-t/.context-meter-state.json','utf8');process.stdout.write(String(JSON.parse(s).inputTokens||0))}catch(_){process.stdout.write('0')}")
21
+ T0_PCT=$(node -e "try{const tb=require('./bin/token-budget.js');process.stdout.write(String(tb.getSessionStatus('.').pct||0))}catch(_){process.stdout.write('0')}")
22
+ ```
23
+
24
+ **After each phase spawn — record the bracket:**
25
+
26
+ ```bash
27
+ T1_TOKENS=$(node -e "try{const s=require('fs').readFileSync('.gsd-t/.context-meter-state.json','utf8');process.stdout.write(String(JSON.parse(s).inputTokens||0))}catch(_){process.stdout.write('0')}")
28
+ T1_PCT=$(node -e "try{const tb=require('./bin/token-budget.js');process.stdout.write(String(tb.getSessionStatus('.').pct||0))}catch(_){process.stdout.write('0')}")
29
+ node -e "require('./bin/token-telemetry.js').recordSpawn({timestamp:new Date().toISOString(),milestone:process.env.GSD_T_MILESTONE||'',command:'gsd-t-wave',phase:'${PHASE:-}',step:'${STEP:-}',domain:'${DOMAIN:-}',domain_type:'${DOMAIN_TYPE:-}',task:'${TASK:-}',model:'${MODEL:-sonnet}',duration_s:${DURATION:-0},input_tokens_before:${T0_TOKENS},input_tokens_after:${T1_TOKENS},tokens_consumed:${T1_TOKENS}-${T0_TOKENS},context_window_pct_before:${T0_PCT},context_window_pct_after:${T1_PCT},outcome:'${OUTCOME:-success}',halt_type:${HALT_TYPE:-null},escalated_via_advisor:${ESCALATED_VIA_ADVISOR:-false}})" 2>/dev/null || true
30
+ ```
31
+
32
+ The bracket is additive to the existing `.gsd-t/token-log.md` OBSERVABILITY LOGGING rows. Both sinks coexist — token-log.md is human-readable with context percentage, token-metrics.jsonl is machine-readable with the full 18-field schema for `gsd-t metrics --tokens/--halts/--context-window` aggregation.
33
+
34
+ ## Step 0: Runway Check (MANDATORY — before any other work in a fresh session)
35
+
36
+ Count the wave's total task count (sum of atomic tasks across domains in the current wave). Then run via Bash:
37
+
38
+ ```bash
39
+ node -e "
40
+ const r = require('./bin/runway-estimator.js').estimateRunway({
41
+ command: 'gsd-t-wave',
42
+ domain_type: '',
43
+ remaining_tasks: {N},
44
+ projectDir: '.'
45
+ });
46
+ console.log(JSON.stringify(r, null, 2));
47
+ if (!r.can_start) {
48
+ console.log('⛔ Insufficient runway — projected ' + r.projected_end_pct + '% (current ' + r.current_pct + '%, ' + r.pct_per_task + '%/task, ' + r.confidence + ' confidence, ' + r.confidence_basis + ' records)');
49
+ console.log('Auto-spawning headless to continue in a fresh context.');
50
+ const s = require('./bin/headless-auto-spawn.js').autoSpawnHeadless({
51
+ command: 'gsd-t-wave', args: [], continue_from: '.'
52
+ });
53
+ console.log('Session ID: ' + s.id);
54
+ console.log('Status: tail ' + s.logPath);
55
+ console.log('');
56
+ console.log('Your interactive session remains idle — you can use it for other work.');
57
+ console.log('You will be notified when the headless run completes.');
58
+ process.exit(0);
59
+ }
60
+ "
61
+ ```
62
+
63
+ If `can_start === false`, the headless continuation has already been spawned and the interactive session must stop here. Do NOT proceed to Step 0.1.
64
+
65
+ **Contract**: `.gsd-t/contracts/runway-estimator-contract.md` v1.0.0; stop threshold (85%) mirrors `.gsd-t/contracts/token-budget-contract.md` v3.0.0.
66
+
67
+ ## Step 0.1: Verify Context Gate Readiness (MANDATORY — first thing in a fresh session)
6
68
 
7
69
  Run via Bash:
8
70
 
9
71
  ```bash
10
- node bin/task-counter.cjs reset
72
+ node -e "const tb = require('./bin/token-budget.js'); const s = tb.getSessionStatus('.'); console.log(JSON.stringify(s));"
11
73
  ```
12
74
 
13
- This clears `.gsd-t/.task-counter` so the new wave session starts at 0. The gate logic is in the Phase Agent Spawn Pattern below — it forces a /clear-and-resume after N phase spawns to prevent the wave orchestrator from itself running out of context. Default N=5, override per-project via `.gsd-t/task-counter-config.json` (`{"limit":8}`) or env `GSD_T_TASK_LIMIT=8`.
75
+ This calls `getSessionStatus()` (v2.0.0) which reads `.gsd-t/.context-meter-state.json` produced by the Context Meter PostToolUse hook. The returned `threshold` drives the gate logic in the Phase Agent Spawn Pattern below — it enforces the three-band stop boundary (85%) so the wave orchestrator itself never runs out of context mid-wave. When the state file is absent or stale, the call falls back to a historical heuristic from `.gsd-t/token-log.md`. Band boundaries and `modelWindowSize` are configured in `.gsd-t/context-meter-config.json` and `bin/token-budget.js` (THRESHOLDS constant).
14
76
 
15
77
  ## Step 1: Load State (Lightweight)
16
78
 
@@ -96,10 +158,10 @@ If STACK_RULES is empty (no templates/stacks/ dir or no matches), skip silently.
96
158
  Run via Bash:
97
159
  `node -e "const tb = require('./bin/token-budget.js'); const s = tb.getSessionStatus('.'); process.stdout.write(s.threshold);" 2>/dev/null`
98
160
 
161
+ Three-band model per `token-budget-contract.md` v3.0.0 (never silently degrade quality):
99
162
  - `normal` or file missing → proceed normally
100
- - `downgrade` → apply model overrides from `getDegradationActions()` in the phase agent prompt
101
- - `conserve` → checkpoint progress, skip non-essential phases (test-sync if code unchanged, discuss if already done)
102
- - `stop` → checkpoint all progress and output: "Token budget exhausted — wave paused. Resume after session reset." then halt
163
+ - `warn` (≥70%) log the warning to `.gsd-t/token-log.md` and proceed at full quality; **do NOT downgrade models or skip phases** — M35 removed that behavior intentionally
164
+ - `stop` (≥85%) → checkpoint all progress and output: "Wave orchestrator context gate reached ({pct}%). Progress saved. Resume after session reset." then halt
103
165
 
104
166
  **OBSERVABILITY LOGGING (MANDATORY) — repeat for every phase spawn:**
105
167
  Before spawning — run via Bash:
@@ -126,29 +188,31 @@ Task agent (subagent_type: "general-purpose", mode: "bypassPermissions"):
126
188
  After phase agent returns — run via Bash:
127
189
  `T_END=$(date +%s) && DT_END=$(date +"%Y-%m-%d %H:%M") && DURATION=$((T_END-T_START))`
128
190
 
129
- **Wave Orchestrator Phase-Count Gate (MANDATORY) — replaces the broken context-percent check from v2.74.x:**
191
+ **Wave Orchestrator Context Gate (MANDATORY) — real-count measurement via Context Meter state file:**
130
192
 
131
193
  Run via Bash AFTER each phase agent returns:
132
194
 
133
195
  ```bash
134
- node bin/task-counter.cjs increment phase
196
+ node -e "const tb=require('./bin/token-budget.js'); const s=tb.getSessionStatus('.'); process.stdout.write(JSON.stringify(s)); if(s.threshold==='stop')process.exit(10); if(s.threshold==='warn')process.exit(13);"
135
197
  ```
136
198
 
137
- Read the JSON status the command prints. If `should_stop` is `true` (or the command's exit code is `10`):
138
- 1. Save checkpoint to `.gsd-t/progress.md` — record which phases are complete, which remain.
139
- 2. Output exactly: `⏸️ Wave orchestrator phase-count gate reached ({count}/{limit} phases in this session). Progress saved. Run /clear then /user:gsd-t-wave to continue from the next phase.`
140
- 3. **STOP the wave loop.** Do NOT spawn the next phase agent. The next session resumes from saved state.
199
+ The JSON on stdout contains `{consumed, estimated_remaining, pct, threshold}` — capture `pct` as `{CTX_PCT}` for the token-log row.
200
+
201
+ Exit-code handling (three-band model per `token-budget-contract.md` v3.0.0):
202
+ - `0` (normal, <70%) proceed to the next phase at full quality.
203
+ - `13` (warn, 70–85%) → log the warning to `.gsd-t/token-log.md` and proceed to the next phase at full quality. **Never downgrade models or skip phases** — the runway estimator (m35-runway-estimator) is responsible for halting cleanly before reaching `stop`.
204
+ - `10` (stop, ≥85%) → STOP the wave loop. Save checkpoint to `.gsd-t/progress.md` — record which phases are complete, which remain. Call `autoSpawnHeadless({command: 'gsd-t-wave', args, projectDir})` — this spawns a fresh headless session that auto-resumes via `/gsd-t-resume` without any manual `/clear`. Output: `⏸️ Wave orchestrator context gate reached ({pct}% of model window) — handing off to a fresh headless session (ID: {id}). Progress saved.` Return cleanly. Do NOT spawn the next phase agent, do NOT exit with a special code — the handoff is the success path.
141
205
 
142
- The wave orchestrator shares the same `bin/task-counter.cjs` counter as the execute orchestrator. Each phase spawn (PARTITION, DISCUSS, PLAN, IMPACT, EXECUTE, TEST-SYNC, INTEGRATE, VERIFY+COMPLETE, DOC-RIPPLE) increments the counter by 1. With the default limit of 5, a wave will run at most 5 phase agents per session before forcing a /clear-and-resume typically two sessions per full wave. Override via `.gsd-t/task-counter-config.json` (`{"limit":8}`) or `GSD_T_TASK_LIMIT=8`.
206
+ As of v2.0.0 (M34), the wave orchestrator reads the SAME `bin/token-budget.js` real-source measurement as the execute orchestrator — both trace back to `.gsd-t/.context-meter-state.json` produced by the Context Meter PostToolUse hook. Each phase spawn (PARTITION, DISCUSS, PLAN, IMPACT, EXECUTE, TEST-SYNC, INTEGRATE, VERIFY+COMPLETE, DOC-RIPPLE) causes post-call updates to the state file, so each subsequent gate check reflects the real context consumption trajectory. When the state file is absent or stale, the call falls back to the historical heuristic.
143
207
 
144
- The previous version of this gate relied on `CLAUDE_CONTEXT_TOKENS_USED`/`_MAX` env vars which Claude Code does not export that check was inert and let the orchestrator drain context until forced compaction. The deterministic on-disk counter has the same intent (force a /clear before context runs out) but actually works.
208
+ The previous v1.x version relied on an environment-variable-based context check which Claude Code never populated; v2.74.12 stood in a proxy task counter; v2.0.0 (M34) retires both and uses the real count_tokens measurement.
145
209
 
146
- **On wave entry**, the wave orchestrator runs `node bin/task-counter.cjs reset` exactly once (see Step 0 it is the very first thing the wave does in a fresh session). The reset is the SIGNAL that this is a clean post-/clear session.
210
+ **On wave entry**, Step 0 runs `getSessionStatus()` once for a readiness confirmation. No counter reset is needed `/clear` plus the next tool call will cause the Context Meter hook to refresh the state file with the new session's starting `input_tokens`.
147
211
 
148
- Append to `.gsd-t/token-log.md` (create with header `| Datetime-start | Datetime-end | Command | Step | Model | Duration(s) | Notes | Domain | Task | Tasks-Since-Reset |` if missing):
149
- `| {DT_START} | {DT_END} | gsd-t-wave | {PHASE} | sonnet | {DURATION}s | phase: {PHASE} | | | {COUNTER} |`
212
+ Append to `.gsd-t/token-log.md` (create with header `| Datetime-start | Datetime-end | Command | Step | Model | Duration(s) | Notes | Domain | Task | Ctx% |` if missing):
213
+ `| {DT_START} | {DT_END} | gsd-t-wave | {PHASE} | sonnet | {DURATION}s | phase: {PHASE} | | | {CTX_PCT} |`
150
214
 
151
- Where `{COUNTER}` is the `count` field from the JSON the increment command just printed.
215
+ Where `{CTX_PCT}` is the `pct` field from the JSON the getSessionStatus command just printed.
152
216
 
153
217
  ### Phase Sequence
154
218
 
@@ -114,6 +114,9 @@ GSD-T reads all state files and tells you exactly where you left off.
114
114
 
115
115
  | Command | Purpose | Auto |
116
116
  |---------|---------|------|
117
+ | `/user:gsd-t-unattended` | Launch detached supervisor — runs active milestone to completion with zero human intervention | Manual |
118
+ | `/user:gsd-t-unattended-watch` | Watch tick — fires every 270s via ScheduleWakeup, reports supervisor status | Auto |
119
+ | `/user:gsd-t-unattended-stop` | Touch stop sentinel — supervisor halts after current worker finishes | Manual |
117
120
  | `/user:gsd-t-wave` | Full cycle, auto-advances all phases | Manual |
118
121
  | `/user:gsd-t-status` | Cross-domain progress view with token breakdown, global ELO and cross-project rankings | Manual |
119
122
  | `/user:gsd-t-resume` | Restore context, continue | Manual |
@@ -286,6 +289,20 @@ Drop a `.md` file into `templates/stacks/` to add a new stack. Files prefixed wi
286
289
 
287
290
  ---
288
291
 
292
+ ## Unattended Execution (M36)
293
+
294
+ Run the active milestone to completion over hours or days with zero human intervention. The unattended supervisor is an OS-level process that spawns `claude -p` workers in a relay — each worker runs in a fresh context window, completing one round of wave tasks before handing off to the next. The supervisor survives `/clear`, terminal close, and sleep/wake cycles (macOS/Linux; see `docs/unattended-windows-caveats.md` for Windows).
295
+
296
+ **Relay model**: Each iteration spawns a fresh `claude -p` session with a compact prompt derived from `.gsd-t/progress.md` state. The supervisor waits for the worker to exit, records the exit code in `state.json`, runs safety checks (gutter detection, blocker sentinel scan), and spawns the next worker. Workers never overlap. Context rot is impossible — each worker starts clean.
297
+
298
+ **Watch loop**: `/user:gsd-t-unattended` starts an in-session ScheduleWakeup loop that ticks every 270 seconds. Each tick reads `state.json` and `supervisor.pid` to render a live progress block. When the supervisor reaches a terminal state (`done`, `failed`, `stopped`), the watch loop stops rescheduling and prints a final summary. A `/clear` + `/user:gsd-t-resume` transparently re-attaches: the resume command checks for a live `supervisor.pid` and re-starts the watch loop automatically.
299
+
300
+ **Safety rails**: Branch protection (refuses to run on `main`/`master`/`release/*` by default), dirty-tree check (whitelists GSD-T runtime files), per-iteration gutter detection (repeated error patterns, file thrash, no-progress stall), wall-clock and iteration caps, and a blocker sentinel that halts on unrecoverable worker errors.
301
+
302
+ **Contract**: `.gsd-t/contracts/unattended-supervisor-contract.md` v1.0.0 is the authoritative reference for the state file schema, exit codes, CLI surface, and platform matrix.
303
+
304
+ ---
305
+
289
306
  ## Headless Mode
290
307
 
291
308
  Run GSD-T non-interactively in CI/CD pipelines or automated workflows.
@@ -423,6 +440,58 @@ Recommended `.gsd-t/config.json`:
423
440
  }
424
441
  ```
425
442
 
443
+ ### Context Meter (M34)
444
+
445
+ The Context Meter is a PostToolUse hook that runs after every tool call, streams the current Claude Code transcript to the Anthropic `count_tokens` API, and writes the exact input-token count and threshold band to `.gsd-t/.context-meter-state.json`. The GSD-T orchestrator (`gsd-t-execute`, `gsd-t-wave`, `gsd-t-quick`, `gsd-t-integrate`, `gsd-t-debug`) reads this state file via `token-budget.getSessionStatus()` as the authoritative context-burn signal — replacing the v2.74.12 task-counter proxy.
446
+
447
+ Setup:
448
+
449
+ 1. **Export an API key** — `export ANTHROPIC_API_KEY="sk-ant-..."` (free-tier key is sufficient; `count_tokens` is inexpensive).
450
+ 2. **Install the hook** — `npx @tekyzinc/gsd-t install` (registers the PostToolUse hook globally) then `npx @tekyzinc/gsd-t init` in each project (copies the hook runtime and config template).
451
+ 3. **Verify with doctor** — `npx @tekyzinc/gsd-t doctor` hard-gates on API key presence, hook registration, script existence, config validity, and a live `count_tokens` dry-run.
452
+ 4. **Check status** — `npx @tekyzinc/gsd-t status` shows a Context line with `{pct}% of {window} tokens ({band}) — last check {time ago}`.
453
+
454
+ `.gsd-t/context-meter-config.json` controls the meter:
455
+
456
+ ```json
457
+ {
458
+ "enabled": true,
459
+ "apiKeyEnvVar": "ANTHROPIC_API_KEY",
460
+ "modelWindowSize": 200000,
461
+ "thresholdPct": 85,
462
+ "checkFrequency": 1
463
+ }
464
+ ```
465
+
466
+ Threshold bands used by the orchestrator gate (v3.0.0 three-band model as of M35 / v2.76.10):
467
+
468
+ | Band | Range | Orchestrator action |
469
+ |-----------|-------------|-----------------------------------------------------------------|
470
+ | normal | 0–69% | Proceed as normal |
471
+ | warn | 70–84% | Surface warning; runway estimator refuses if projection crosses |
472
+ | stop | ≥85% | Halt cleanly; headless auto-spawn continues in a fresh context |
473
+
474
+ **Zero silent quality degradation.** There is no `downgrade` or `conserve` band anymore. When the runway estimator (`bin/runway-estimator.js`) projects a run will cross the 85% stop threshold, the command refuses to start. Instead of printing "please `/clear` and resume," it calls `bin/headless-auto-spawn.js` which detaches a child process to continue the work. The interactive session receives a single ⛔ banner and exits cleanly; the user is never blocked.
475
+
476
+ **Per-phase model selection** — see `bin/model-selector.js` for the declarative rules table (≥13 phase mappings). Each command file carries a `## Model Assignment` block documenting which phases run on haiku / sonnet / opus. Complexity signals (`cross_module_refactor`, `security_boundary`, `data_loss_risk`, `contract_design`) escalate sonnet→opus at plan time.
477
+
478
+ **`/advisor` escalation** — mid-phase escalation channel. If `/advisor` is programmable in the runtime, subagents invoke it directly; otherwise, the convention-based fallback (`bin/advisor-integration.js`) appends a `missed_escalation` marker to `.gsd-t/token-log.md` that surfaces at `gsd-t-reflect`. See `.gsd-t/contracts/model-selection-contract.md` v1.0.0.
479
+
480
+ **`gsd-t metrics` CLI** — `gsd-t metrics --tokens [--by model,command,phase,milestone,domain,domain_type]` aggregates `.gsd-t/token-metrics.jsonl` into a count/total/mean/median/p95 table. `gsd-t metrics --halts` breaks halts down by type (`clean`, `runway-refusal`, `headless-handoff`, `native-compact`). `gsd-t metrics --tokens --context-window` buckets spawns by `context_window_pct_before` in 10% increments. See `.gsd-t/contracts/token-telemetry-contract.md` v1.0.0.
481
+
482
+ **Optimization apply/reject** — `/user:gsd-t-optimization-apply {ID}` promotes a pending recommendation; `/user:gsd-t-optimization-reject {ID} [--reason "text"]` dismisses it with a 5-milestone cooldown. Both are idempotent. See `commands/gsd-t-optimization-apply.md` and `commands/gsd-t-optimization-reject.md`.
483
+
484
+ **Observability logging columns** — as of M34, the `.gsd-t/token-log.md` header includes `Ctx%` (the real session-wide context percentage at the time of the subagent spawn) replacing the earlier `Tasks-Since-Reset` column. The old column was a proxy count of how many tasks had run since the last `/clear`; the new column is the actual measurement.
485
+
486
+ **Upgrading from pre-M34** — `gsd-t update-all` handles the migration automatically:
487
+ - Copies the hook script, runtime files, and config template into every registered project
488
+ - Runs a one-time task-counter retirement (`bin/task-counter.cjs` + `.task-counter*` files deleted, `.gsd-t/.task-counter-retired-v1` marker written)
489
+ - Idempotent on second run
490
+
491
+ After upgrading, you **must** set `ANTHROPIC_API_KEY` or `gsd-t doctor` will fail.
492
+
493
+ **Historical note on v2.74.12–13**: between 2026-03 and 2026-04, the orchestrator used `bin/task-counter.cjs` as a proxy — it assumed N tasks ≈ M% context used. That was itself a replacement for an earlier env-var-based check (`CLAUDE_CONTEXT_TOKENS_USED` / `CLAUDE_CONTEXT_TOKENS_MAX`) that never worked because Claude Code does not export those vars. The Context Meter (v2.75.10, M34) is the first version that measures context burn from the authoritative source: the Anthropic API itself.
494
+
426
495
  ---
427
496
 
428
497
  ## License
@@ -68,14 +68,14 @@ The framework has no runtime — it is consumed entirely by Claude Code's slash
68
68
 
69
69
  ### Headless Mode (M23 — complete)
70
70
  - **doHeadless(args)**: Dispatch function for the `headless` CLI subcommand.
71
- - **doHeadlessExec(command, cmdArgs, flags)**: Wraps `claude -p "/user:gsd-t-{command}"` via `execFileSync`. Verifies claude CLI availability, enforces timeout, writes log file if `--log` requested. Returns structured JSON if `--json` flag set.
71
+ - **doHeadlessExec(command, cmdArgs, flags)**: Wraps `claude -p "/gsd-t-{command}"` via `execFileSync`. Verifies claude CLI availability, enforces timeout, writes log file if `--log` requested. Returns structured JSON if `--json` flag set. (M36 Phase 0: prompt form is `/gsd-t-X`, NOT `/user:gsd-t-X` — non-interactive mode rejects the `/user:` namespace prefix.)
72
72
  - **parseHeadlessFlags(args)**: Extracts `--json`, `--timeout=N`, `--log` from raw args. Returns `{ flags, positional }`.
73
- - **buildHeadlessCmd(command, cmdArgs)**: Builds the `/user:gsd-t-{command}` prompt string.
74
- - **mapHeadlessExitCode(processExitCode, output)**: Maps process exit code + output text patterns to GSD-T exit codes (0–4).
73
+ - **buildHeadlessCmd(command, cmdArgs)**: Builds the bare `/gsd-t-{command}` prompt string. Interactive-mode `/user:` prefix deliberately omitted — see `.gsd-t/M36-spike-findings.md` Spike A.
74
+ - **mapHeadlessExitCode(processExitCode, output)**: Maps process exit code + output text patterns to GSD-T exit codes (0–5).
75
75
  - **headlessLogPath(projectDir, timestamp)**: Generates `.gsd-t/headless-{timestamp}.log` path.
76
76
  - **doHeadlessQuery(type)**: Dispatches to one of 7 query functions. All pure Node.js file reads, no LLM calls, <100ms.
77
77
  - **Query functions** (7): `queryStatus`, `queryDomains`, `queryContracts`, `queryDebt`, `queryContext`, `queryBacklog`, `queryGraph` — each reads corresponding `.gsd-t/` file and returns typed JSON result.
78
- - **Exit codes**: 0=success, 1=verify-fail, 2=context-budget-exceeded, 3=error, 4=blocked-needs-human
78
+ - **Exit codes**: 0=success, 1=verify-fail, 2=context-budget-exceeded, 3=error, 4=blocked-needs-human, 5=command-dispatch-failed (M36 Phase 0 — `claude -p` returned `Unknown command:` for the slash command; caller should treat as a bug not a transient failure)
79
79
  - **CI/CD examples**: `docs/ci-examples/github-actions.yml` (GitHub Actions), `docs/ci-examples/gitlab-ci.yml` (GitLab CI)
80
80
 
81
81
  ### Compaction-Proof Debug Loop (M29 — complete)
@@ -272,6 +272,83 @@ QA runs inline or as Task subagent depending on phase (M10 refactor). Removed fr
272
272
  - **Resource limits**: Heartbeat stdin capped at 1MB, HTTP responses capped at 1MB (M5), 5s/8s timeouts, 7-day file cleanup
273
273
  - **Wave security**: `bypassPermissions` mode documented with attack surface analysis and mitigations (M5)
274
274
 
275
+ ## Unattended Supervisor (M36)
276
+
277
+ The unattended supervisor is a cross-session relay engine that runs an active GSD-T milestone to completion over hours or days without human intervention. It spans the boundary between the interactive Claude session and the OS process layer.
278
+
279
+ ### Component Diagram
280
+
281
+ ```
282
+ Interactive Claude session
283
+ └── /user:gsd-t-unattended (launch command)
284
+ ├── Pre-flight safety checks (branch, dirty tree)
285
+ └── spawn(detached) → Supervisor process (bin/gsd-t-unattended.js)
286
+ ├── writes .gsd-t/.unattended/supervisor.pid
287
+ ├── writes .gsd-t/.unattended/state.json (atomic rewrite each iter)
288
+ ├── appends .gsd-t/.unattended/run.log (worker stdout+stderr)
289
+ ├── checks .gsd-t/.unattended/stop (sentinel — presence = halt)
290
+ └── relay loop:
291
+ spawnSync('claude -p "/gsd-t-resume"')
292
+ → worker exits → post-worker safety check → next iter
293
+
294
+ In-session watch loop (every 270s via ScheduleWakeup)
295
+ └── /user:gsd-t-unattended-watch
296
+ ├── reads supervisor.pid (kill -0 liveness)
297
+ ├── reads state.json (status, iter, lastTick)
298
+ └── reschedules or reports final status
299
+ ```
300
+
301
+ ### State Directory Layout
302
+
303
+ ```
304
+ .gsd-t/.unattended/
305
+ ├── supervisor.pid — Integer PID. Exists ONLY while supervisor is alive.
306
+ ├── state.json — Live state snapshot. Atomically rewritten between iterations.
307
+ ├── run.log — Append-only worker stdout+stderr. Never truncated during a run.
308
+ ├── stop — Sentinel file. Absence = run. Presence = user-requested stop.
309
+ └── config.json — Optional per-project config overrides (maxIterations, hours, etc.)
310
+ ```
311
+
312
+ Sibling: `.gsd-t/.handoff/` — owned by M35-gap-fixes for single-shot handoff locks (see below).
313
+
314
+ ### Contract
315
+
316
+ `.gsd-t/contracts/unattended-supervisor-contract.md` v1.0.0 — authoritative source for: state schema, status enum, exit-code table, launch handshake, watch tick decision tree, resume auto-reattach handshake, stop mechanism, safety-rails hook points, and CLI surface.
317
+
318
+ ### Platform Abstraction Layer (`bin/gsd-t-unattended-platform.js`)
319
+
320
+ Exports four cross-platform functions:
321
+
322
+ | Export | macOS | Linux | Windows |
323
+ |--------|-------|-------|---------|
324
+ | `spawnSupervisor(args)` | `spawn(node, ...)` detached | same | same (`windowsHide:true`) |
325
+ | `preventSleep()` | `caffeinate -i` subprocess | `systemd-inhibit` or no-op | no-op (not supported — see docs/unattended-windows-caveats.md) |
326
+ | `releaseSleep(handle)` | kill caffeinate PID | release inhibit or no-op | no-op |
327
+ | `notify(title, msg, level)` | `osascript` | `notify-send` | no-op |
328
+ | `resolveClaudePath()` | PATH lookup | PATH lookup | `claude.cmd` via PATH |
329
+
330
+ ### Safety Rails (`bin/gsd-t-unattended-safety.js`)
331
+
332
+ Called at four supervisor hook points (pre-launch, supervisor-init, pre-worker, post-worker):
333
+
334
+ - **Gutter detection**: stall pattern — repeated identical errors or no file changes for N iterations
335
+ - **Blocker sentinels**: scan worker stdout for unrecoverable-error markers (`BLOCKED_NEEDS_HUMAN`, `DISPATCH_FAILED`)
336
+ - **Iteration cap**: `maxIterations` guard (default 200)
337
+ - **Wall-clock cap**: `hours` guard (default 24h)
338
+ - **Branch/dirty-tree pre-flight**: refuses to start on protected branches or uncleaned worktrees
339
+
340
+ Each check returns `{ ok, reason?, code? }`. A `false` result halts with `status = 'failed'` and the corresponding exit code (6=gutter, 7=protected-branch, 8=dirty-tree).
341
+
342
+ ### Handoff-Lock Primitive (`bin/handoff-lock.js`)
343
+
344
+ Closes the M35 parent/child race in `bin/headless-auto-spawn.js`. When the runway estimator fires `autoSpawnHeadless()`, the parent session writes a lock file in `.gsd-t/.handoff/` before spawning the child and removes it only after the child has confirmed PID + state-ready. Prevents the child from beginning execution before the parent has cleanly exited — eliminating the race where both sessions wrote to the same `.gsd-t/` files simultaneously.
345
+
346
+ ### Resume Auto-Reattach
347
+
348
+ `/user:gsd-t-resume` Step 0 checks for a live supervisor before any other resume logic. If `supervisor.pid` exists and `kill -0` succeeds and `state.json.status` is non-terminal, the resume command skips normal resume flow entirely, prints the current watch block, and calls `ScheduleWakeup(270, '/user:gsd-t-unattended-watch', ...)`. The user transparently re-enters the watch loop without any manual step.
349
+
350
+ ---
351
+
275
352
  ## Design Decisions
276
353
 
277
354
  | Date | Decision | Rationale | Alternatives Considered |
@@ -368,6 +445,101 @@ During partition, UI/frontend projects automatically receive `.gsd-t/contracts/d
368
445
 
369
446
  When Playwright MCP is registered in Claude Code settings, QA agents get 3 minutes of interactive exploration and Red Team gets 5 minutes after all scripted tests pass. Findings are tagged `[EXPLORATORY]` in qa-issues.md and red-team-report.md, and tracked separately in QA calibration (category key: `exploratory` — does NOT count against scripted pass/fail ratio). Silent skip when Playwright MCP absent. Wired into: execute, quick, integrate, debug.
370
447
 
448
+ ## Context Meter Architecture (M34, v2.75.10+)
449
+
450
+ The Context Meter is the authoritative source for session context-burn measurement in GSD-T. It replaces the v2.74.12 `bin/task-counter.cjs` proxy (and the pre-v2.74.12 `CLAUDE_CONTEXT_TOKENS_USED` env-var approach, which never worked because Claude Code does not export those vars).
451
+
452
+ **Data flow:**
453
+
454
+ ```
455
+ Claude Code tool call finishes
456
+
457
+
458
+ PostToolUse hook (~/.claude/settings.json registered)
459
+
460
+
461
+ scripts/gsd-t-context-meter.js (runMeter)
462
+
463
+ ├── 1. loadConfig(.gsd-t/context-meter-config.json)
464
+ ├── 2. check-frequency gate — short-circuits if tool-call % freq != 0
465
+ ├── 3. parseTranscript(hook.transcript_path)
466
+ │ → { system, messages } shaped for count_tokens
467
+ ├── 4. countTokens({apiKey, model, system, messages, timeoutMs:200})
468
+ │ → POST https://api.anthropic.com/v1/messages/count_tokens
469
+ │ → 200 { input_tokens } | failure → null
470
+ ├── 5. computePct(inputTokens, modelWindowSize)
471
+ ├── 6. bandFor(pct) → "normal" | "warn" | "stop" (v3.0.0 three-band model)
472
+ └── 7. atomic write .gsd-t/.context-meter-state.json
473
+ { version, timestamp, inputTokens, modelWindowSize, pct, threshold, checkCount, lastError? }
474
+
475
+
476
+ bin/token-budget.js getSessionStatus(projectDir) ── v3.0.0: normal/warn/stop only
477
+
478
+ ├── readContextMeterState(dir)
479
+ │ if fresh (timestamp within 5 min):
480
+ │ return { consumed, estimated_remaining, pct, threshold }
481
+ │ else: null
482
+
483
+ └── fallback: readSessionConsumed(dir) from .gsd-t/token-log.md (heuristic)
484
+
485
+
486
+ bin/runway-estimator.js estimateRunway({command, domain_type, remaining_tasks})
487
+ │ reads current_pct from .context-meter-state.json
488
+ │ queries .gsd-t/token-metrics.jsonl for historical pct-delta per spawn
489
+ │ projects current_pct + pct_per_task × remaining_tasks × skew
490
+ │ confidence: high ≥50 records, medium ≥10, low <10 (+1.25× skew)
491
+ │ returns {can_start, projected_end_pct, confidence, recommendation}
492
+
493
+ Command file Step 0 — runway gate (execute/wave/quick/integrate/debug):
494
+ if (!decision.can_start) {
495
+ print ⛔ banner
496
+ autoSpawnHeadless({command, continue_from: '.'}) ── bin/headless-auto-spawn.js
497
+ process.exit(0) ── never prompts user
498
+ } else {
499
+ proceed to Step 0.1 (Verify Context Gate Readiness) and Step 1
500
+ }
501
+
502
+
503
+ bin/headless-auto-spawn.js (when refused)
504
+ │ detached child: node bin/gsd-t.js headless {command} --log
505
+ │ child.unref(); interactive session returns immediately
506
+ │ writes .gsd-t/headless-sessions/{id}.json (status: "running")
507
+ │ 2s poll watcher: process.kill(pid, 0) → mac osascript notification on exit
508
+
509
+
510
+ Orchestrator Context Gate — v3.0.0 semantics:
511
+ normal → proceed
512
+ warn → log to .gsd-t/token-log.md, proceed at full quality (informational only)
513
+ stop → halt cleanly, runway estimator hands off to headless-auto-spawn
514
+ ```
515
+
516
+ **Key constraints:**
517
+ - **Fail-open**: every stage catches errors and writes a partial state file. Never crashes Claude Code.
518
+ - **No message content in state or log files** — only token counts, band names, error codes.
519
+ - **Never logs or writes the API key** anywhere.
520
+ - **State staleness window**: 5 minutes — after that, heuristic fallback takes over.
521
+ - **Hook latency budget**: 200ms (timeoutMs on the HTTP call), enforced by `req.setTimeout` + `req.destroy()`.
522
+
523
+ **Contracts:**
524
+ - `.gsd-t/contracts/context-meter-contract.md` — schema, state file format, hook I/O
525
+ - `.gsd-t/contracts/context-observability-contract.md` v2.0.0 — Ctx% as the real session-wide signal (replaces Tasks-Since-Reset)
526
+ - `.gsd-t/contracts/token-budget-contract.md` v3.0.0 — three-band stop-at-85 (M35 clean break from v2.0.0)
527
+ - `.gsd-t/contracts/token-telemetry-contract.md` v1.0.0 — per-spawn 18-field JSONL at `.gsd-t/token-metrics.jsonl`
528
+ - `.gsd-t/contracts/runway-estimator-contract.md` v1.0.0 — pre-flight projection, confidence grading, refusal/headless handoff
529
+ - `.gsd-t/contracts/headless-auto-spawn-contract.md` v1.0.0 — detached continuation, session schema, macOS notification channel
530
+ - `.gsd-t/contracts/model-selection-contract.md` v1.0.0 — per-phase tier mapping + complexity-signal escalation, consumed by `bin/model-selector.js`
531
+
532
+ **M35 supporting components** (outside the context-meter dataflow):
533
+ - `bin/model-selector.js` — declarative rules table mapping phases to haiku/sonnet/opus; consulted at plan time, never at runtime under pressure
534
+ - `bin/token-optimizer.js` — at `complete-milestone`, scans the last 3 milestones of `.gsd-t/token-metrics.jsonl` and appends recalibration recommendations to `.gsd-t/optimization-backlog.md` (never auto-applied; user promotes via `/user:gsd-t-optimization-apply` or rejects via `/user:gsd-t-optimization-reject` with 5-milestone cooldown)
535
+ - `bin/check-headless-sessions.js` — renders the read-back banner on `/user:gsd-t-resume` and `/user:gsd-t-status` for completed-but-not-yet-surfaced headless sessions
536
+
537
+ **Installer integration** (`bin/gsd-t.js`):
538
+ - `install` / `init` — copy hook runtime, merge PostToolUse entry into `~/.claude/settings.json`, copy config template, prompt for API key (skippable, TTY-only)
539
+ - `doctor` — RED on missing API key, missing hook, missing script, invalid config, failed count_tokens dry-run
540
+ - `status` — displays `Context: {pct}% of {window} tokens ({band}) — last check {rel}` line
541
+ - `update-all` — one-shot task-counter retirement migration (deletes legacy files, writes `.gsd-t/.task-counter-retired-v1` marker)
542
+
371
543
  ## Planned Architecture Changes (M23-M24)
372
544
 
373
545
  **M23: Headless Mode**