@tekyzinc/gsd-t 3.10.11 → 3.10.13
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/CHANGELOG.md +62 -0
- package/bin/gsd-t.js +14 -5
- package/bin/{token-budget.js → token-budget.cjs} +51 -0
- package/commands/gsd-t-audit.md +1 -1
- package/commands/gsd-t-brainstorm.md +1 -1
- package/commands/gsd-t-debug.md +4 -4
- package/commands/gsd-t-discuss.md +1 -1
- package/commands/gsd-t-doc-ripple.md +3 -3
- package/commands/gsd-t-execute.md +13 -13
- package/commands/gsd-t-help.md +1 -1
- package/commands/gsd-t-integrate.md +3 -3
- package/commands/gsd-t-plan.md +1 -1
- package/commands/gsd-t-prd.md +1 -1
- package/commands/gsd-t-quick.md +4 -4
- package/commands/gsd-t-reflect.md +1 -1
- package/commands/gsd-t-resume.md +29 -0
- package/commands/gsd-t-unattended.md +2 -2
- package/commands/gsd-t-verify.md +2 -2
- package/commands/gsd-t-visualize.md +1 -1
- package/commands/gsd-t-wave.md +10 -9
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,68 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to GSD-T are documented here. Updated with each release.
|
|
4
4
|
|
|
5
|
+
## [3.10.13] - 2026-04-15
|
|
6
|
+
|
|
7
|
+
### Fixed — P0 v3.10.12 propagation gap (same regression, downstream projects)
|
|
8
|
+
|
|
9
|
+
**Background**: v3.10.12 shipped the `stale` band fix to `bin/token-budget.js` in the GSD-T repo, but verification revealed the fix was **never visible in any downstream project**. Every command file gate snippet is `require('./bin/token-budget.js')` resolved against the **project cwd** — and no downstream project has a local `bin/token-budget.js` file. `PROJECT_BIN_TOOLS` in `bin/gsd-t.js` (the list that `update-all` copies to each registered project) did not include `token-budget.js`, so downstream projects never received any copy. The require throws `MODULE_NOT_FOUND`, the surrounding `try{…}catch(_){process.stdout.write('0')}` swallows it, and the gate sees `pct: 0` = normal band. Identical failure mode to the original regression.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
- **`bin/token-budget.js` → `bin/token-budget.cjs`** — renamed to `.cjs` so it runs as CommonJS regardless of downstream `package.json` `"type"` field. Some registered projects use `"type": "module"`, which would have broken `require('./bin/token-budget.js')` even if the file were propagated. The `.cjs` extension is the same convention used by all other tools in `PROJECT_BIN_TOOLS` (`archive-progress.cjs`, `log-tail.cjs`, `context-budget-audit.cjs`, `context-meter-config.cjs`).
|
|
13
|
+
- **`bin/gsd-t.js`** `PROJECT_BIN_TOOLS` — appended `"token-budget.cjs"`. Now `update-all` copies the file to every registered project's `bin/` on update.
|
|
14
|
+
- **All 17 command files** referencing `./bin/token-budget.js` — updated to `./bin/token-budget.cjs`: `gsd-t-execute`, `gsd-t-wave`, `gsd-t-quick`, `gsd-t-debug`, `gsd-t-integrate`, `gsd-t-doc-ripple`, `gsd-t-verify`, `gsd-t-plan`, `gsd-t-discuss`, `gsd-t-visualize`, `gsd-t-reflect`, `gsd-t-brainstorm`, `gsd-t-audit`, `gsd-t-prd`, `gsd-t-resume`, `gsd-t-unattended`, `gsd-t-help`.
|
|
15
|
+
- **`test/token-budget.test.js`** — require path updated to `../bin/token-budget.cjs`. All 1228 tests pass.
|
|
16
|
+
|
|
17
|
+
### Why this matters
|
|
18
|
+
Without this patch, v3.10.12's `stale` band fix is **dead code in every downstream project**. The command files fail the require, catch silently, and the gate goes back to reporting 0% normal — exactly the invisible failure mode that caused the M36 regression in the first place. The two patches are a single logical fix; shipping v3.10.12 alone was incomplete.
|
|
19
|
+
|
|
20
|
+
### Verification
|
|
21
|
+
- `npm test` → 1228/1228 pass (same baseline as v3.10.12)
|
|
22
|
+
- No runtime references to `token-budget.js` remain under `commands/`
|
|
23
|
+
- `bin/token-budget.cjs` is 13867 bytes (verbatim copy of the v3.10.12 `token-budget.js`)
|
|
24
|
+
- `PROJECT_BIN_TOOLS` now has 5 entries — `update-all` will copy to all 15 registered projects on next invocation
|
|
25
|
+
|
|
26
|
+
## [3.10.12] - 2026-04-15
|
|
27
|
+
|
|
28
|
+
### Fixed — P0 context meter regression (M36 /compact incidents)
|
|
29
|
+
|
|
30
|
+
**Background**: During M36 execution the user hit Claude Code's native `/compact` prompt multiple times — the exact scenario M34's Context Meter was built to prevent. Audit of `.gsd-t/.context-meter-state.json` revealed `checkCount=2102` with `pct=0` and `lastError: missing_key` forever. Every one of 2102 PostToolUse hook calls had silently failed at the `ANTHROPIC_API_KEY` check and returned `{}` per the fail-open invariant. `token-budget.getSessionStatus()` read `pct: 0` and reported `threshold: "normal"` to the gate, so the gate was **blind since installation** with no user-visible alarm at any layer.
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
- **`bin/token-budget.js`** — fourth `stale` band in `getSessionStatus()`. When the state file exists but is dead (`lastError` set, `timestamp` null, state older than 5 min, or JSON corrupt), returns `{threshold: "stale", deadReason}` with one of `meter_error:missing_key`, `meter_error:api_error`, `meter_error:parse_failure`, `meter_error:no_transcript`, `meter_never_measured`, `meter_state_stale`, `state_file_corrupt`, `state_file_unreadable`. Previously fell through to the heuristic (which reported 0% and was indistinguishable from a healthy fresh session).
|
|
34
|
+
- **`bin/token-budget.js`** — `buildBandResponse()` handles the `stale` band with a loud message pointing at `gsd-t doctor` and `ANTHROPIC_API_KEY`.
|
|
35
|
+
- **`commands/gsd-t-resume.md`** — new Step 0.6 "Context Meter Health Check" runs after the headless read-back banner and before state loading. If the meter is stale, prints a prominent warning, runs `gsd-t doctor` inline, and refuses to auto-advance into gated commands (`execute`, `wave`, `integrate`, `quick`, `debug`) until fixed.
|
|
36
|
+
- **`.gsd-t/contracts/context-meter-contract.md` v1.1.0** — new §"Stale Band and Resume Gating" documents the regression, the fix, and the mandatory resume-time health check. Also adds a "measurement only, never inference" rule to the configuration section clarifying that the API key named in `apiKeyEnvVar` must never be used for `/v1/messages` inference — inference always runs through the Claude Code subscription.
|
|
37
|
+
- **`.gsd-t/contracts/token-budget-contract.md` v3.1.0** — fourth band added to the threshold table with explicit "gate treats stale as exit-10 stop but does NOT auto-spawn" semantics (a fresh session would have the same broken guardrail).
|
|
38
|
+
|
|
39
|
+
### Changed
|
|
40
|
+
- **`commands/gsd-t-execute.md`** — Step 3.5 gate snippet (Orchestrator Context Gate) and Step 7 per-domain context gate re-check now exit 10 on `s.threshold==='stop'||s.threshold==='stale'`. Both sites print different user-facing messages for each band: `stop` → "halt cleanly, hand off to runway estimator"; `stale` → "run `gsd-t doctor` and fix the cause".
|
|
41
|
+
- **`commands/gsd-t-wave.md`** — Wave Orchestrator Context Gate snippet now exits 10 on `stale` in addition to `stop`. The `stale` path does NOT call `autoSpawnHeadless()` — a fresh session would have the same broken guardrail.
|
|
42
|
+
- **`bin/gsd-t.js`** — `showStatusContextMeter()` promotes the dead-meter line from a dim ignorable whisper to a red `✗ CONTEXT METER DEAD` alarm with actionable fix instructions (explicitly calls out "measurement only — inference stays on Claude Code subscription" when the cause is `missing_key`). This is the line the user would have seen on every `gsd-t status` run during M36 if it had been loud enough to notice.
|
|
43
|
+
|
|
44
|
+
### Root cause and the 6 hypotheses
|
|
45
|
+
|
|
46
|
+
The continue-here file for this session listed 6 plausible failure modes. The audit disproved 5 of them and proved the sixth:
|
|
47
|
+
1. ❌ Gate only fires at subagent-spawn boundaries — FALSE, coverage is broad (16 command files call `getSessionStatus`, `execute` alone has 13 call sites).
|
|
48
|
+
2. ❌ Coverage holes in command files — FALSE, the 4 gated commands (`execute`, `wave`, `integrate`, `quick`, `debug`) all call the gate.
|
|
49
|
+
3. ❌ `.gsd-t/.context-meter-state.json` stale due to silent hook failure — PARTIALLY; the state was not stale, it was **never fresh**. The file had `timestamp: null` after 2102 checks.
|
|
50
|
+
4. ✅ **PostToolUse hook silent failure on missing `ANTHROPIC_API_KEY` — CONFIRMED root cause.** The hook's `runMeter()` step 5 checks the env var, writes `lastError: {code: "missing_key"}`, persists the state, and returns `{}`. This is correct per the hook's fail-open invariant. But nothing downstream was LOUD about it: `token-budget.js` fell through to the heuristic; the gate saw `threshold: "normal"`; `gsd-t status` printed a dim line.
|
|
51
|
+
5. ❌ Session-tokens vs main-transcript measurement gap — MOOT, you can't have a measurement gap if you never measured.
|
|
52
|
+
6. ❌ 85% stop band too thin — MOOT for the same reason.
|
|
53
|
+
|
|
54
|
+
The v3.10.12 fix targets only the real root cause: **make the gate fail loud when the meter is dead**, and add a resume-time health check so future sessions can't silently run without the guardrail.
|
|
55
|
+
|
|
56
|
+
### User-visible fix
|
|
57
|
+
|
|
58
|
+
If you see `✗ CONTEXT METER DEAD` on `gsd-t status` or `⚠ Context meter is DEAD` from `gsd-t-resume`, set `ANTHROPIC_API_KEY` in your shell profile:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
echo 'export ANTHROPIC_API_KEY="sk-ant-..."' >> ~/.zshrc
|
|
62
|
+
source ~/.zshrc
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
**Important:** this key is used ONLY for `count_tokens` measurement via the PostToolUse hook and `gsd-t doctor` diagnostics. It is NEVER used for model inference — inference always runs through the Claude Code subscription. The contract `context-meter-contract.md` v1.1.0 Rule #3 enforces this.
|
|
66
|
+
|
|
5
67
|
## [3.10.11] - 2026-04-15
|
|
6
68
|
|
|
7
69
|
### Added
|
package/bin/gsd-t.js
CHANGED
|
@@ -1515,11 +1515,20 @@ function showStatusContextMeter() {
|
|
|
1515
1515
|
return;
|
|
1516
1516
|
}
|
|
1517
1517
|
|
|
1518
|
-
// Error case:
|
|
1519
|
-
|
|
1518
|
+
// Error case: lastError set. Promoted from DIM to ERROR in v3.10.12.
|
|
1519
|
+
// Before: a silent dim line nobody read. After: a red alarm so users see
|
|
1520
|
+
// that the context-window guardrail is DEAD (M36 regression fix).
|
|
1521
|
+
if (state.lastError) {
|
|
1520
1522
|
const code = (state.lastError && state.lastError.code) || "unknown";
|
|
1521
|
-
const rel = state.timestamp ? formatRelativeTime(state.timestamp) : "
|
|
1522
|
-
log(` ${
|
|
1523
|
+
const rel = state.timestamp ? formatRelativeTime(state.timestamp) : "never measured";
|
|
1524
|
+
log(` ${RED}${BOLD}✗ CONTEXT METER DEAD${RESET} ${RED}— error: ${code}, last check: ${rel}${RESET}`);
|
|
1525
|
+
log(` ${RED}The context-window guardrail is NOT working. Long sessions will hit /compact.${RESET}`);
|
|
1526
|
+
if (code === "missing_key") {
|
|
1527
|
+
log(` ${YELLOW}Fix: export ANTHROPIC_API_KEY in your shell profile${RESET}`);
|
|
1528
|
+
log(` ${YELLOW} (measurement only — inference stays on Claude Code subscription)${RESET}`);
|
|
1529
|
+
} else {
|
|
1530
|
+
log(` ${YELLOW}Fix: run 'gsd-t doctor' for diagnostics${RESET}`);
|
|
1531
|
+
}
|
|
1523
1532
|
return;
|
|
1524
1533
|
}
|
|
1525
1534
|
|
|
@@ -1966,7 +1975,7 @@ function updateSingleProject(projectDir, counts) {
|
|
|
1966
1975
|
// Bin tools that should ship with every registered project. Listed here so adding
|
|
1967
1976
|
// a new tool only requires appending to this array. Use .cjs extension so they
|
|
1968
1977
|
// always run as CommonJS regardless of the project's package.json "type" field.
|
|
1969
|
-
const PROJECT_BIN_TOOLS = ["archive-progress.cjs", "log-tail.cjs", "context-budget-audit.cjs", "context-meter-config.cjs"];
|
|
1978
|
+
const PROJECT_BIN_TOOLS = ["archive-progress.cjs", "log-tail.cjs", "context-budget-audit.cjs", "context-meter-config.cjs", "token-budget.cjs"];
|
|
1970
1979
|
|
|
1971
1980
|
function copyBinToolsToProject(projectDir, projectName) {
|
|
1972
1981
|
const projectBinDir = path.join(projectDir, "bin");
|
|
@@ -115,6 +115,21 @@ function getSessionStatus(projectDir) {
|
|
|
115
115
|
const threshold = resolveThreshold(pct);
|
|
116
116
|
return { consumed, estimated_remaining, pct, threshold };
|
|
117
117
|
}
|
|
118
|
+
// Meter exists but is dead (file present, API key missing, parse/API error,
|
|
119
|
+
// or timestamp stale). Return a `stale` band so callers halt instead of
|
|
120
|
+
// silently running with a blind gate. This is the fix for the M36 /compact
|
|
121
|
+
// regression where checkCount=2102 but every hook call failed fail-open.
|
|
122
|
+
const dead = readContextMeterDead(dir);
|
|
123
|
+
if (dead) {
|
|
124
|
+
const window = dead.modelWindowSize > 0 ? dead.modelWindowSize : 200000;
|
|
125
|
+
return {
|
|
126
|
+
consumed: 0,
|
|
127
|
+
estimated_remaining: window,
|
|
128
|
+
pct: 0,
|
|
129
|
+
threshold: "stale",
|
|
130
|
+
deadReason: dead.reason,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
118
133
|
return getSessionStatusHeuristic(dir);
|
|
119
134
|
}
|
|
120
135
|
|
|
@@ -127,12 +142,42 @@ function readContextMeterState(dir) {
|
|
|
127
142
|
if (!s.timestamp) return null;
|
|
128
143
|
const age = Date.now() - Date.parse(s.timestamp);
|
|
129
144
|
if (isNaN(age) || age > STATE_STALE_MS || age < 0) return null;
|
|
145
|
+
if (s.lastError && typeof s.lastError === "object") return null;
|
|
130
146
|
return s;
|
|
131
147
|
} catch (_) {
|
|
132
148
|
return null;
|
|
133
149
|
}
|
|
134
150
|
}
|
|
135
151
|
|
|
152
|
+
// Return { reason, modelWindowSize } when the state file exists but the meter
|
|
153
|
+
// is not producing a fresh, clean reading. Returns null when the file is
|
|
154
|
+
// missing entirely (no meter installed — fall through to heuristic).
|
|
155
|
+
function readContextMeterDead(dir) {
|
|
156
|
+
try {
|
|
157
|
+
const fp = path.join(dir, STATE_FILE_REL);
|
|
158
|
+
if (!fs.existsSync(fp)) return null;
|
|
159
|
+
const raw = fs.readFileSync(fp, "utf8");
|
|
160
|
+
const s = JSON.parse(raw);
|
|
161
|
+
if (!s || typeof s !== "object") {
|
|
162
|
+
return { reason: "state_file_corrupt", modelWindowSize: 0 };
|
|
163
|
+
}
|
|
164
|
+
const window = Number.isInteger(s.modelWindowSize) ? s.modelWindowSize : 0;
|
|
165
|
+
if (s.lastError && typeof s.lastError === "object" && typeof s.lastError.code === "string") {
|
|
166
|
+
return { reason: `meter_error:${s.lastError.code}`, modelWindowSize: window };
|
|
167
|
+
}
|
|
168
|
+
if (!s.timestamp) {
|
|
169
|
+
return { reason: "meter_never_measured", modelWindowSize: window };
|
|
170
|
+
}
|
|
171
|
+
const age = Date.now() - Date.parse(s.timestamp);
|
|
172
|
+
if (isNaN(age) || age < 0 || age > STATE_STALE_MS) {
|
|
173
|
+
return { reason: "meter_state_stale", modelWindowSize: window };
|
|
174
|
+
}
|
|
175
|
+
return null;
|
|
176
|
+
} catch (_) {
|
|
177
|
+
return { reason: "state_file_unreadable", modelWindowSize: 0 };
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
136
181
|
function getSessionStatusHeuristic(dir) {
|
|
137
182
|
const window = 200000;
|
|
138
183
|
const consumed = readSessionConsumed(dir);
|
|
@@ -218,6 +263,12 @@ function buildBandResponse(band, pct) {
|
|
|
218
263
|
pct: safePct,
|
|
219
264
|
message: `Context ${safePct.toFixed(1)}% — stop band (≥${STOP_THRESHOLD_PCT}%). Halt cleanly; hand off to runway estimator / headless auto-spawn.`,
|
|
220
265
|
};
|
|
266
|
+
case "stale":
|
|
267
|
+
return {
|
|
268
|
+
band: "stale",
|
|
269
|
+
pct: safePct,
|
|
270
|
+
message: `Context meter is DEAD — no fresh measurements. Gate treats this as STOP. Run: gsd-t doctor. Fix the cause (usually missing ANTHROPIC_API_KEY) before running gated commands.`,
|
|
271
|
+
};
|
|
221
272
|
case "normal":
|
|
222
273
|
default:
|
|
223
274
|
return {
|
package/commands/gsd-t-audit.md
CHANGED
|
@@ -22,7 +22,7 @@ Read CLAUDE.md and .gsd-t/progress.md for project context, then execute gsd-t-au
|
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
After subagent returns — run via Bash:
|
|
25
|
-
`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.
|
|
25
|
+
`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.cjs'); process.stdout.write(String(tb.getSessionStatus('.').pct||'N/A'))" 2>/dev/null || echo "N/A")`
|
|
26
26
|
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):
|
|
27
27
|
`| {DT_START} | {DT_END} | gsd-t-audit | Step 0 | sonnet | {DURATION}s | audit: {args summary} | | | {CTX_PCT} |`
|
|
28
28
|
|
|
@@ -123,7 +123,7 @@ Do NOT proceed to Step 5 until this synthesis is complete.
|
|
|
123
123
|
```
|
|
124
124
|
|
|
125
125
|
After team completes — run via Bash:
|
|
126
|
-
`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.
|
|
126
|
+
`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.cjs'); process.stdout.write(String(tb.getSessionStatus('.').pct||'N/A'))" 2>/dev/null || echo "N/A")`
|
|
127
127
|
Append to `.gsd-t/token-log.md` (create with header `| Datetime-start | Datetime-end | Command | Step | Model | Duration(s) | Notes | Ctx% |` if missing):
|
|
128
128
|
`| {DT_START} | {DT_END} | gsd-t-brainstorm | Step 3 | sonnet | {DURATION}s | deep research: {topic summary} | {CTX_PCT} |`
|
|
129
129
|
|
package/commands/gsd-t-debug.md
CHANGED
|
@@ -19,14 +19,14 @@ Per `.gsd-t/contracts/token-telemetry-contract.md` v1.0.0. Every Task subagent s
|
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
21
|
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')}")
|
|
22
|
-
T0_PCT=$(node -e "try{const tb=require('./bin/token-budget.
|
|
22
|
+
T0_PCT=$(node -e "try{const tb=require('./bin/token-budget.cjs');process.stdout.write(String(tb.getSessionStatus('.').pct||0))}catch(_){process.stdout.write('0')}")
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
**After each spawn — record the bracket:**
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
28
|
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')}")
|
|
29
|
-
T1_PCT=$(node -e "try{const tb=require('./bin/token-budget.
|
|
29
|
+
T1_PCT=$(node -e "try{const tb=require('./bin/token-budget.cjs');process.stdout.write(String(tb.getSessionStatus('.').pct||0))}catch(_){process.stdout.write('0')}")
|
|
30
30
|
node -e "require('./bin/token-telemetry.js').recordSpawn({timestamp:new Date().toISOString(),milestone:process.env.GSD_T_MILESTONE||'',command:'gsd-t-debug',phase:'debug',step:'${STEP:-}',domain:'${DOMAIN:-}',domain_type:'${DOMAIN_TYPE:-}',task:'${TASK:-}',model:'${MODEL:-opus}',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
|
|
31
31
|
```
|
|
32
32
|
|
|
@@ -213,7 +213,7 @@ Lead: Wait for all three researchers to complete. Then synthesize:
|
|
|
213
213
|
```
|
|
214
214
|
|
|
215
215
|
After team completes — run via Bash:
|
|
216
|
-
`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.
|
|
216
|
+
`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.cjs'); process.stdout.write(String(tb.getSessionStatus('.').pct||'N/A'))" 2>/dev/null || echo "N/A")`
|
|
217
217
|
Append to `.gsd-t/token-log.md`:
|
|
218
218
|
`| {DT_START} | {DT_END} | gsd-t-debug | Step 1.5 | sonnet | {DURATION}s | deep research loop break: {issue summary} | {CTX_PCT} |`
|
|
219
219
|
|
|
@@ -506,7 +506,7 @@ Spawn Task subagent (general-purpose, model: opus):
|
|
|
506
506
|
After subagent returns — run via Bash:
|
|
507
507
|
```
|
|
508
508
|
T_END=$(date +%s) && DT_END=$(date +"%Y-%m-%d %H:%M") && DURATION=$((T_END-T_START))
|
|
509
|
-
CTX_PCT=$(node -e "try{const tb=require('./bin/token-budget.
|
|
509
|
+
CTX_PCT=$(node -e "try{const tb=require('./bin/token-budget.cjs'); process.stdout.write(String(tb.getSessionStatus('.').pct))}catch(_){process.stdout.write('N/A')}")
|
|
510
510
|
```
|
|
511
511
|
Append to `.gsd-t/token-log.md`:
|
|
512
512
|
`| {DT_START} | {DT_END} | gsd-t-debug | Red Team | opus | {DURATION}s | {VERDICT} — {N} bugs found | | | {CTX_PCT} |`
|
|
@@ -79,7 +79,7 @@ Lead: Synthesize into decisions and update contracts.
|
|
|
79
79
|
```
|
|
80
80
|
|
|
81
81
|
After team completes — run via Bash:
|
|
82
|
-
`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.
|
|
82
|
+
`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.cjs'); process.stdout.write(String(tb.getSessionStatus('.').pct||'N/A'))" 2>/dev/null || echo "N/A")`
|
|
83
83
|
Append to `.gsd-t/token-log.md` (create with header `| Datetime-start | Datetime-end | Command | Step | Model | Duration(s) | Notes | Ctx% |` if missing):
|
|
84
84
|
`| {DT_START} | {DT_END} | gsd-t-discuss | Step 3 | sonnet | {DURATION}s | team discuss: {topic summary} | {CTX_PCT} |`
|
|
85
85
|
|
|
@@ -17,14 +17,14 @@ Per `.gsd-t/contracts/token-telemetry-contract.md` v1.0.0. Every Task subagent s
|
|
|
17
17
|
|
|
18
18
|
```bash
|
|
19
19
|
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')}")
|
|
20
|
-
T0_PCT=$(node -e "try{const tb=require('./bin/token-budget.
|
|
20
|
+
T0_PCT=$(node -e "try{const tb=require('./bin/token-budget.cjs');process.stdout.write(String(tb.getSessionStatus('.').pct||0))}catch(_){process.stdout.write('0')}")
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
**After each spawn — record the bracket:**
|
|
24
24
|
|
|
25
25
|
```bash
|
|
26
26
|
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')}")
|
|
27
|
-
T1_PCT=$(node -e "try{const tb=require('./bin/token-budget.
|
|
27
|
+
T1_PCT=$(node -e "try{const tb=require('./bin/token-budget.cjs');process.stdout.write(String(tb.getSessionStatus('.').pct||0))}catch(_){process.stdout.write('0')}")
|
|
28
28
|
node -e "require('./bin/token-telemetry.js').recordSpawn({timestamp:new Date().toISOString(),milestone:process.env.GSD_T_MILESTONE||'',command:'gsd-t-doc-ripple',phase:'doc-ripple',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
|
|
29
29
|
```
|
|
30
30
|
|
|
@@ -127,7 +127,7 @@ After subagent returns — run via Bash:
|
|
|
127
127
|
`T_END=$(date +%s) && DT_END=$(date +"%Y-%m-%d %H:%M") && DURATION=$((T_END-T_START))`
|
|
128
128
|
|
|
129
129
|
Read the real context% from the Context Meter state file:
|
|
130
|
-
`CTX_PCT=$(node -e "try{const tb=require('./bin/token-budget.
|
|
130
|
+
`CTX_PCT=$(node -e "try{const tb=require('./bin/token-budget.cjs'); process.stdout.write(String(tb.getSessionStatus('.').pct))}catch(_){process.stdout.write('N/A')}")`
|
|
131
131
|
|
|
132
132
|
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):
|
|
133
133
|
`| {DT_START} | {DT_END} | gsd-t-doc-ripple | Step 5 | {model} | {DURATION}s | update:{document} | doc-ripple | — | {CTX_PCT} |`
|
|
@@ -23,14 +23,14 @@ Per `.gsd-t/contracts/token-telemetry-contract.md` v1.0.0. Every Task subagent s
|
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
25
|
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')}")
|
|
26
|
-
T0_PCT=$(node -e "try{const tb=require('./bin/token-budget.
|
|
26
|
+
T0_PCT=$(node -e "try{const tb=require('./bin/token-budget.cjs');process.stdout.write(String(tb.getSessionStatus('.').pct||0))}catch(_){process.stdout.write('0')}")
|
|
27
27
|
```
|
|
28
28
|
|
|
29
29
|
**After each spawn — record the bracket:**
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
32
|
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')}")
|
|
33
|
-
T1_PCT=$(node -e "try{const tb=require('./bin/token-budget.
|
|
33
|
+
T1_PCT=$(node -e "try{const tb=require('./bin/token-budget.cjs');process.stdout.write(String(tb.getSessionStatus('.').pct||0))}catch(_){process.stdout.write('0')}")
|
|
34
34
|
node -e "require('./bin/token-telemetry.js').recordSpawn({timestamp:new Date().toISOString(),milestone:process.env.GSD_T_MILESTONE||'',command:'gsd-t-execute',phase:'${PHASE:-execute}',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
|
|
35
35
|
```
|
|
36
36
|
|
|
@@ -74,7 +74,7 @@ If `can_start === false`, the Step 0 block above has already spawned the headles
|
|
|
74
74
|
Run via Bash:
|
|
75
75
|
|
|
76
76
|
```bash
|
|
77
|
-
node -e "const tb = require('./bin/token-budget.
|
|
77
|
+
node -e "const tb = require('./bin/token-budget.cjs'); const s = tb.getSessionStatus('.'); console.log(JSON.stringify(s));"
|
|
78
78
|
```
|
|
79
79
|
|
|
80
80
|
This calls `getSessionStatus()` (v2.0.0) which reads `.gsd-t/.context-meter-state.json` produced by the Context Meter PostToolUse hook. If the state file is fresh (timestamp within 5 min), you get real `pct` and `threshold` values; if missing or stale, the call falls back to the historical heuristic from `.gsd-t/token-log.md`.
|
|
@@ -191,7 +191,7 @@ Where `{CTX_PCT}` is the current `pct` value returned by `getSessionStatus()` (S
|
|
|
191
191
|
**Token Budget Check (before dispatching each domain's tasks):**
|
|
192
192
|
|
|
193
193
|
Run via Bash:
|
|
194
|
-
`node -e "const tb = require('./bin/token-budget.
|
|
194
|
+
`node -e "const tb = require('./bin/token-budget.cjs'); const s = tb.getSessionStatus('.'); const d = tb.getDegradationActions(s.threshold, '.'); process.stdout.write(JSON.stringify({band: d.band, pct: d.pct, message: d.message}));" 2>/dev/null`
|
|
195
195
|
|
|
196
196
|
Apply the result (three-band model per `token-budget-contract.md` v3.0.0 — never silently degrade quality):
|
|
197
197
|
- `band: 'normal'` or file missing → proceed with standard model assignments
|
|
@@ -529,7 +529,7 @@ Report back:
|
|
|
529
529
|
|
|
530
530
|
6. **Per-domain Red Team** — invoke Step 5.5 (Red Team) NOW for this domain. This is the first place Red Team runs in v2.74.12 — there is no global post-execute Red Team anymore. If Red Team returns FAIL, fix bugs and re-run before proceeding to the next domain (max 2 fix-and-verify cycles); if bugs persist, log to `.gsd-t/deferred-items.md` and present to user.
|
|
531
531
|
|
|
532
|
-
7. **Context gate re-check** — run `node -e "const tb=require('./bin/token-budget.
|
|
532
|
+
7. **Context gate re-check** — run `node -e "const tb=require('./bin/token-budget.cjs'); const s=tb.getSessionStatus('.'); if(s.threshold==='stop'||s.threshold==='stale')process.exit(10); if(s.threshold==='warn')process.exit(13);"`. If exit code is `10`, follow the Step 3.5 STOP procedure now (do NOT spawn the next domain). `stale` means the context meter is dead (usually missing `ANTHROPIC_API_KEY`) and is treated as STOP — print `⚠ Context meter DEAD — run 'gsd-t doctor' and fix before continuing` and halt. If exit code is `13`, log the warning and proceed at full quality for the next domain (no model overrides, no phase skips — quality is never silently degraded).
|
|
533
533
|
|
|
534
534
|
### Team Mode (when agent teams are enabled)
|
|
535
535
|
Spawn teammates for domains within the same wave. Only domains in the same wave can run in parallel — do not spawn teammates for domains in different waves simultaneously. Each teammate uses the **domain task-dispatcher pattern** — one subagent per task within their domain (same as solo mode).
|
|
@@ -675,18 +675,18 @@ Cleanup is not optional — orphaned worktrees waste disk space and can confuse
|
|
|
675
675
|
|
|
676
676
|
## Step 3.5: Orchestrator Context Gate (MANDATORY)
|
|
677
677
|
|
|
678
|
-
The orchestrator MUST check `getSessionStatus()` BEFORE every task subagent spawn AND immediately AFTER every domain completes. This is the real context-burn guardrail. As of v2.0.0 (M34), `bin/token-budget.
|
|
678
|
+
The orchestrator MUST check `getSessionStatus()` BEFORE every task subagent spawn AND immediately AFTER every domain completes. This is the real context-burn guardrail. As of v2.0.0 (M34), `bin/token-budget.cjs` reads `.gsd-t/.context-meter-state.json` — the live count_tokens-based `input_tokens` measurement produced by the Context Meter PostToolUse hook. When the state file is fresh (timestamp within 5 min), thresholds reflect the ACTUAL context window utilization; when absent or stale, the call falls back to the historical heuristic from `.gsd-t/token-log.md`.
|
|
679
679
|
|
|
680
680
|
**Before each task spawn — gate check:**
|
|
681
681
|
|
|
682
682
|
```bash
|
|
683
|
-
node -e "const tb=require('./bin/token-budget.
|
|
683
|
+
node -e "const tb=require('./bin/token-budget.cjs'); const s=tb.getSessionStatus('.'); process.stdout.write(JSON.stringify(s)); if(s.threshold==='stop'||s.threshold==='stale')process.exit(10); if(s.threshold==='warn')process.exit(13);"
|
|
684
684
|
```
|
|
685
685
|
|
|
686
|
-
Exit code semantics (three-band model per `token-budget-contract.md` v3.0.0):
|
|
686
|
+
Exit code semantics (three-band model per `token-budget-contract.md` v3.0.0, extended with a fourth `stale` guard in v3.10.12):
|
|
687
687
|
- `0` → `normal` band (< 70% ctx). Proceed with standard model assignments.
|
|
688
688
|
- `13` → `warn` band (70–85%). Log the warning to `.gsd-t/token-log.md` and proceed at full quality. **Never downgrade models or skip phases** — M35 removed that behavior intentionally. If the projected runway is insufficient, the runway estimator (m35-runway-estimator) will halt cleanly before reaching `stop`.
|
|
689
|
-
- `10` → `stop` band (≥ 85%)
|
|
689
|
+
- `10` → `stop` band (≥ 85%) **OR `stale` band (meter dead)**. STOP immediately. Do NOT spawn the next task. Jump straight to the STOP procedure below. For `stale`, also print `⚠ Context meter DEAD — run 'gsd-t doctor' and fix the cause (usually missing ANTHROPIC_API_KEY) before resuming` and halt the session — `stale` is not a resumable halt, it means the guardrail is BROKEN, not that the session is full.
|
|
690
690
|
|
|
691
691
|
The JSON on stdout contains `{consumed, estimated_remaining, pct, threshold}` — capture `pct` as `{CTX_PCT}` for the token-log `Ctx%` column on the NEXT spawn.
|
|
692
692
|
|
|
@@ -709,13 +709,13 @@ Run the same command again. The fresh reading reflects post-task consumption (th
|
|
|
709
709
|
|
|
710
710
|
**Configuring threshold bands:**
|
|
711
711
|
|
|
712
|
-
Band boundaries (`warn=70`, `stop=85`) are defined in `bin/token-budget.
|
|
712
|
+
Band boundaries (`warn=70`, `stop=85`) are defined in `bin/token-budget.cjs` (`WARN_THRESHOLD_PCT` / `STOP_THRESHOLD_PCT` constants) and documented in `.gsd-t/contracts/token-budget-contract.md` v3.0.0. The `modelWindowSize` used for the denominator comes from `.gsd-t/context-meter-config.json` (default `200000`). Override the window size there if running against a different model. There is no per-session env-var override — the real-time measurement supersedes the need for one.
|
|
713
713
|
|
|
714
714
|
**On resume (Step 0 — first thing the orchestrator does in a fresh session):**
|
|
715
715
|
|
|
716
716
|
Step 0 runs `getSessionStatus()` once for readiness confirmation. The reading should be fresh (the Context Meter hook fires on every tool call), so the gate immediately reflects the new session's starting pct — typically near 0 since `/clear` resets the conversation.
|
|
717
717
|
|
|
718
|
-
This gate replaces the v2.74.12 task counter proxy and the (never-functional) v1.x env-var check. It is fail-safe: if `bin/token-budget.
|
|
718
|
+
This gate replaces the v2.74.12 task counter proxy and the (never-functional) v1.x env-var check. It is fail-safe: if `bin/token-budget.cjs` or the state file is unreadable for any reason, `getSessionStatus()` throws and the gate exits non-zero (treated as STOP) rather than silently allowing unlimited spawns.
|
|
719
719
|
|
|
720
720
|
## Step 4: Checkpoint Handling
|
|
721
721
|
|
|
@@ -791,7 +791,7 @@ and summary, and the full comparison table per the protocol's Step 7."
|
|
|
791
791
|
```
|
|
792
792
|
|
|
793
793
|
After subagent returns — run via Bash:
|
|
794
|
-
`T_END=$(date +%s) && DT_END=$(date +"%Y-%m-%d %H:%M") && DURATION=$((T_END-T_START)) && CTX_PCT=$(node -e "try{const tb=require('./bin/token-budget.
|
|
794
|
+
`T_END=$(date +%s) && DT_END=$(date +"%Y-%m-%d %H:%M") && DURATION=$((T_END-T_START)) && CTX_PCT=$(node -e "try{const tb=require('./bin/token-budget.cjs'); process.stdout.write(String(tb.getSessionStatus('.').pct))}catch(_){process.stdout.write('N/A')}")`
|
|
795
795
|
Append to `.gsd-t/token-log.md`:
|
|
796
796
|
`| {DT_START} | {DT_END} | gsd-t-execute | Design Verify | opus | {DURATION}s | {VERDICT} — {MATCH}/{TOTAL} elements for {domain-name} | | | {CTX_PCT} |`
|
|
797
797
|
|
|
@@ -844,7 +844,7 @@ attack categories exhausted, and the path to the written
|
|
|
844
844
|
```
|
|
845
845
|
|
|
846
846
|
After subagent returns — run via Bash:
|
|
847
|
-
`T_END=$(date +%s) && DT_END=$(date +"%Y-%m-%d %H:%M") && DURATION=$((T_END-T_START)) && CTX_PCT=$(node -e "try{const tb=require('./bin/token-budget.
|
|
847
|
+
`T_END=$(date +%s) && DT_END=$(date +"%Y-%m-%d %H:%M") && DURATION=$((T_END-T_START)) && CTX_PCT=$(node -e "try{const tb=require('./bin/token-budget.cjs'); process.stdout.write(String(tb.getSessionStatus('.').pct))}catch(_){process.stdout.write('N/A')}")`
|
|
848
848
|
Append to `.gsd-t/token-log.md`:
|
|
849
849
|
`| {DT_START} | {DT_END} | gsd-t-execute | Red Team | opus | {DURATION}s | {VERDICT} — {N} bugs found in {domain-name} | | | {CTX_PCT} |`
|
|
850
850
|
|
package/commands/gsd-t-help.md
CHANGED
|
@@ -369,7 +369,7 @@ Use these when user asks for help on a specific command:
|
|
|
369
369
|
- **Summary**: Harness self-audit — analyze cost and benefit of GSD-T enforcement components (QA, Red Team, doc-ripple, token budget), with optional shadow mode to measure overhead
|
|
370
370
|
- **Auto-invoked**: No
|
|
371
371
|
- **Creates**: `.gsd-t/audit-report.md`
|
|
372
|
-
- **Reads**: `.gsd-t/metrics/`, `.gsd-t/token-log.md`, `bin/component-registry.js`, `bin/qa-calibrator.js`, `bin/token-budget.
|
|
372
|
+
- **Reads**: `.gsd-t/metrics/`, `.gsd-t/token-log.md`, `bin/component-registry.js`, `bin/qa-calibrator.js`, `bin/token-budget.cjs`
|
|
373
373
|
- **Use when**: Reviewing whether enforcement components are adding value or overhead; shadow mode measures impact without disabling components; `--disable {component}` temporarily disables one component for comparison
|
|
374
374
|
|
|
375
375
|
### headless --debug-loop
|
|
@@ -19,14 +19,14 @@ Per `.gsd-t/contracts/token-telemetry-contract.md` v1.0.0. Every Task subagent s
|
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
21
|
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')}")
|
|
22
|
-
T0_PCT=$(node -e "try{const tb=require('./bin/token-budget.
|
|
22
|
+
T0_PCT=$(node -e "try{const tb=require('./bin/token-budget.cjs');process.stdout.write(String(tb.getSessionStatus('.').pct||0))}catch(_){process.stdout.write('0')}")
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
**After each spawn — record the bracket:**
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
28
|
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')}")
|
|
29
|
-
T1_PCT=$(node -e "try{const tb=require('./bin/token-budget.
|
|
29
|
+
T1_PCT=$(node -e "try{const tb=require('./bin/token-budget.cjs');process.stdout.write(String(tb.getSessionStatus('.').pct||0))}catch(_){process.stdout.write('0')}")
|
|
30
30
|
node -e "require('./bin/token-telemetry.js').recordSpawn({timestamp:new Date().toISOString(),milestone:process.env.GSD_T_MILESTONE||'',command:'gsd-t-integrate',phase:'integrate',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
|
|
31
31
|
```
|
|
32
32
|
|
|
@@ -292,7 +292,7 @@ Spawn Task subagent (general-purpose, model: opus):
|
|
|
292
292
|
After subagent returns — run via Bash:
|
|
293
293
|
```
|
|
294
294
|
T_END=$(date +%s) && DT_END=$(date +"%Y-%m-%d %H:%M") && DURATION=$((T_END-T_START))
|
|
295
|
-
CTX_PCT=$(node -e "try{const tb=require('./bin/token-budget.
|
|
295
|
+
CTX_PCT=$(node -e "try{const tb=require('./bin/token-budget.cjs'); process.stdout.write(String(tb.getSessionStatus('.').pct))}catch(_){process.stdout.write('N/A')}")
|
|
296
296
|
```
|
|
297
297
|
Append to `.gsd-t/token-log.md`:
|
|
298
298
|
`| {DT_START} | {DT_END} | gsd-t-integrate | Red Team | opus | {DURATION}s | {VERDICT} — {N} bugs found | | | {CTX_PCT} |`
|
package/commands/gsd-t-plan.md
CHANGED
|
@@ -383,7 +383,7 @@ Report: PASS (all checks pass) or FAIL with specific gaps listed."
|
|
|
383
383
|
Before spawning — run via Bash:
|
|
384
384
|
`T_START=$(date +%s) && DT_START=$(date +"%Y-%m-%d %H:%M")`
|
|
385
385
|
After subagent returns — run via Bash:
|
|
386
|
-
`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.
|
|
386
|
+
`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.cjs'); process.stdout.write(String(tb.getSessionStatus('.').pct||'N/A'))" 2>/dev/null || echo "N/A")`
|
|
387
387
|
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):
|
|
388
388
|
`| {DT_START} | {DT_END} | gsd-t-plan | Step 7 | haiku | {DURATION}s | {PASS/FAIL}, iteration {N} | | | {CTX_PCT} |`
|
|
389
389
|
If validation FAIL, append each gap to `.gsd-t/qa-issues.md` (create with header `| Date | Command | Step | Model | Duration(s) | Severity | Finding |` if missing):
|
package/commands/gsd-t-prd.md
CHANGED
|
@@ -23,7 +23,7 @@ Read CLAUDE.md and .gsd-t/progress.md for project context, then execute gsd-t-pr
|
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
After subagent returns — run via Bash:
|
|
26
|
-
`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.
|
|
26
|
+
`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.cjs'); process.stdout.write(String(tb.getSessionStatus('.').pct||'N/A'))" 2>/dev/null || echo "N/A")`
|
|
27
27
|
Append to `.gsd-t/token-log.md` (create with header `| Datetime-start | Datetime-end | Command | Step | Model | Duration(s) | Notes | Ctx% |` if missing):
|
|
28
28
|
`| {DT_START} | {DT_END} | gsd-t-prd | Step 0 | sonnet | {DURATION}s | prd: {topic summary} | {CTX_PCT} |`
|
|
29
29
|
|
package/commands/gsd-t-quick.md
CHANGED
|
@@ -19,14 +19,14 @@ Per `.gsd-t/contracts/token-telemetry-contract.md` v1.0.0. Every Task subagent s
|
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
21
|
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')}")
|
|
22
|
-
T0_PCT=$(node -e "try{const tb=require('./bin/token-budget.
|
|
22
|
+
T0_PCT=$(node -e "try{const tb=require('./bin/token-budget.cjs');process.stdout.write(String(tb.getSessionStatus('.').pct||0))}catch(_){process.stdout.write('0')}")
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
**After each spawn — record the bracket:**
|
|
26
26
|
|
|
27
27
|
```bash
|
|
28
28
|
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')}")
|
|
29
|
-
T1_PCT=$(node -e "try{const tb=require('./bin/token-budget.
|
|
29
|
+
T1_PCT=$(node -e "try{const tb=require('./bin/token-budget.cjs');process.stdout.write(String(tb.getSessionStatus('.').pct||0))}catch(_){process.stdout.write('0')}")
|
|
30
30
|
node -e "require('./bin/token-telemetry.js').recordSpawn({timestamp:new Date().toISOString(),milestone:process.env.GSD_T_MILESTONE||'',command:'gsd-t-quick',phase:'quick',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
|
|
31
31
|
```
|
|
32
32
|
|
|
@@ -78,7 +78,7 @@ Before spawning — run via Bash:
|
|
|
78
78
|
**Token Budget Check (before spawning subagent):**
|
|
79
79
|
|
|
80
80
|
Run via Bash:
|
|
81
|
-
`node -e "const tb = require('./bin/token-budget.
|
|
81
|
+
`node -e "const tb = require('./bin/token-budget.cjs'); const s = tb.getSessionStatus('.'); process.stdout.write(s.threshold);" 2>/dev/null`
|
|
82
82
|
|
|
83
83
|
Apply the result (three-band model per `token-budget-contract.md` v3.0.0 — never silently degrade quality):
|
|
84
84
|
- `normal` or file missing → proceed with default model (sonnet)
|
|
@@ -403,7 +403,7 @@ Spawn Task subagent (general-purpose, model: opus):
|
|
|
403
403
|
After subagent returns — run via Bash:
|
|
404
404
|
```
|
|
405
405
|
T_END=$(date +%s) && DT_END=$(date +"%Y-%m-%d %H:%M") && DURATION=$((T_END-T_START))
|
|
406
|
-
CTX_PCT=$(node -e "try{const tb=require('./bin/token-budget.
|
|
406
|
+
CTX_PCT=$(node -e "try{const tb=require('./bin/token-budget.cjs'); process.stdout.write(String(tb.getSessionStatus('.').pct))}catch(_){process.stdout.write('N/A')}")
|
|
407
407
|
```
|
|
408
408
|
Append to `.gsd-t/token-log.md`:
|
|
409
409
|
`| {DT_START} | {DT_END} | gsd-t-quick | Red Team | opus | {DURATION}s | {VERDICT} — {N} bugs found | | | {CTX_PCT} |`
|
|
@@ -21,7 +21,7 @@ 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)) && CTX_PCT=$(node -e "const tb=require('./bin/token-budget.
|
|
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.cjs'); process.stdout.write(String(tb.getSessionStatus('.').pct||'N/A'))" 2>/dev/null || echo "N/A")`
|
|
25
25
|
|
|
26
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
27
|
`| {DT_START} | {DT_END} | gsd-t-reflect | Step 0 | sonnet | {DURATION}s | retrospective generated | {CTX_PCT} |`
|
package/commands/gsd-t-resume.md
CHANGED
|
@@ -72,6 +72,35 @@ This prints a `## Headless runs since you left` banner listing any completed ses
|
|
|
72
72
|
|
|
73
73
|
Contract: `.gsd-t/contracts/headless-auto-spawn-contract.md` v1.0.0
|
|
74
74
|
|
|
75
|
+
## Step 0.6: Context Meter Health Check (MANDATORY, v3.10.12+)
|
|
76
|
+
|
|
77
|
+
Before loading any other state, verify the Context Meter (M34) is actually alive. A dead meter was the root cause of the M36 `/compact` regression (2026-04-15) — `checkCount=2102` but every hook call failed fail-open because `ANTHROPIC_API_KEY` was unset, and the gate silently reported `pct=0` forever.
|
|
78
|
+
|
|
79
|
+
Run via Bash:
|
|
80
|
+
|
|
81
|
+
```bash
|
|
82
|
+
node -e "
|
|
83
|
+
const tb=require('./bin/token-budget.cjs');
|
|
84
|
+
const s=tb.getSessionStatus('.');
|
|
85
|
+
if (s.threshold === 'stale') {
|
|
86
|
+
console.error('⚠ Context meter is DEAD — reason: ' + (s.deadReason || 'unknown'));
|
|
87
|
+
console.error(' The context-window guardrail is BROKEN. Without it, long sessions will hit /compact silently.');
|
|
88
|
+
console.error(' Fix: set ANTHROPIC_API_KEY in your shell profile (measurement only, never inference).');
|
|
89
|
+
console.error(' Run: gsd-t doctor');
|
|
90
|
+
process.exit(1);
|
|
91
|
+
}
|
|
92
|
+
process.stdout.write('context-meter: ok (' + s.threshold + ', ' + s.pct + '%)\\n');
|
|
93
|
+
" || true
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
If the meter is stale:
|
|
97
|
+
1. **Print the warning** exactly as above (non-fatal — do not halt resume).
|
|
98
|
+
2. **Run `node bin/gsd-t.js doctor`** inline and show the output so the user sees the actionable check list.
|
|
99
|
+
3. **Continue with resume** — but add a prominent `⚠ CONTEXT METER DEAD — gate will treat future gate checks as STOP until fixed` line to your end-of-resume status block so the user cannot miss it.
|
|
100
|
+
4. **Refuse to auto-advance into `execute` / `wave` / `integrate`** until the meter is healthy. If the continue-here file says the next action is one of those gated commands, stop at "meter dead — fix before continuing" instead.
|
|
101
|
+
|
|
102
|
+
Contract: `context-meter-contract.md` v1.1.0 (v3.10.12) — §"Stale Band and Resume Gating"
|
|
103
|
+
|
|
75
104
|
## Step 1: Load Full State (cross-session only)
|
|
76
105
|
|
|
77
106
|
Read in this exact order:
|
|
@@ -223,7 +223,7 @@ If `WARNINGS` were emitted, print them as a non-blocking advisory before proceed
|
|
|
223
223
|
```bash
|
|
224
224
|
T_START=$(date +%s) && DT_START=$(date +"%Y-%m-%d %H:%M")
|
|
225
225
|
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')}")
|
|
226
|
-
T0_PCT=$(node -e "try{const tb=require('./bin/token-budget.
|
|
226
|
+
T0_PCT=$(node -e "try{const tb=require('./bin/token-budget.cjs');process.stdout.write(String(tb.getSessionStatus('.').pct||0))}catch(_){process.stdout.write('0')}")
|
|
227
227
|
```
|
|
228
228
|
|
|
229
229
|
If `--dry-run` was specified, print:
|
|
@@ -273,7 +273,7 @@ Capture `SPAWNED_PID` from the output.
|
|
|
273
273
|
```bash
|
|
274
274
|
T_END=$(date +%s) && DT_END=$(date +"%Y-%m-%d %H:%M") && DURATION=$((T_END-T_START))
|
|
275
275
|
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')}")
|
|
276
|
-
T1_PCT=$(node -e "try{const tb=require('./bin/token-budget.
|
|
276
|
+
T1_PCT=$(node -e "try{const tb=require('./bin/token-budget.cjs');process.stdout.write(String(tb.getSessionStatus('.').pct||0))}catch(_){process.stdout.write('0')}")
|
|
277
277
|
COUNTER=$(node bin/task-counter.cjs status 2>/dev/null | node -e "let s='';process.stdin.on('data',d=>s+=d).on('end',()=>{try{process.stdout.write(String(JSON.parse(s).count||''))}catch(_){process.stdout.write('')}})")
|
|
278
278
|
```
|
|
279
279
|
|
package/commands/gsd-t-verify.md
CHANGED
|
@@ -168,7 +168,7 @@ Before spawning — run via Bash:
|
|
|
168
168
|
`T_START=$(date +%s) && DT_START=$(date +"%Y-%m-%d %H:%M")`
|
|
169
169
|
Spawn a Task subagent to run the full test suite and contract audit.
|
|
170
170
|
After subagent returns — run via Bash:
|
|
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.
|
|
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.cjs'); process.stdout.write(String(tb.getSessionStatus('.').pct||'N/A'))" 2>/dev/null || echo "N/A")`
|
|
172
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
173
|
`| {DT_START} | {DT_END} | gsd-t-verify | Step 4 | haiku | {DURATION}s | test audit + contract review | {CTX_PCT} |`
|
|
174
174
|
Collect all reports, synthesize, create remediation plan.
|
|
@@ -374,7 +374,7 @@ Report back: one-line status summary."
|
|
|
374
374
|
```
|
|
375
375
|
|
|
376
376
|
After subagent returns — run via Bash:
|
|
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.
|
|
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.cjs'); process.stdout.write(String(tb.getSessionStatus('.').pct||'N/A'))" 2>/dev/null || echo "N/A")`
|
|
378
378
|
Append to `.gsd-t/token-log.md`:
|
|
379
379
|
`| {DT_START} | {DT_END} | gsd-t-verify | Step 8 | sonnet | {DURATION}s | auto-complete-milestone | | | {CTX_PCT} |`
|
|
380
380
|
|
|
@@ -21,7 +21,7 @@ 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)) && CTX_PCT=$(node -e "const tb=require('./bin/token-budget.
|
|
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.cjs'); process.stdout.write(String(tb.getSessionStatus('.').pct||'N/A'))" 2>/dev/null || echo "N/A")`
|
|
25
25
|
|
|
26
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
27
|
`| {DT_START} | {DT_END} | gsd-t-visualize | Step 0 | sonnet | {DURATION}s | dashboard launched | {CTX_PCT} |`
|
package/commands/gsd-t-wave.md
CHANGED
|
@@ -18,14 +18,14 @@ Per `.gsd-t/contracts/token-telemetry-contract.md` v1.0.0. Every phase agent spa
|
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
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.
|
|
21
|
+
T0_PCT=$(node -e "try{const tb=require('./bin/token-budget.cjs');process.stdout.write(String(tb.getSessionStatus('.').pct||0))}catch(_){process.stdout.write('0')}")
|
|
22
22
|
```
|
|
23
23
|
|
|
24
24
|
**After each phase spawn — record the bracket:**
|
|
25
25
|
|
|
26
26
|
```bash
|
|
27
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.
|
|
28
|
+
T1_PCT=$(node -e "try{const tb=require('./bin/token-budget.cjs');process.stdout.write(String(tb.getSessionStatus('.').pct||0))}catch(_){process.stdout.write('0')}")
|
|
29
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
30
|
```
|
|
31
31
|
|
|
@@ -69,10 +69,10 @@ If `can_start === false`, the headless continuation has already been spawned and
|
|
|
69
69
|
Run via Bash:
|
|
70
70
|
|
|
71
71
|
```bash
|
|
72
|
-
node -e "const tb = require('./bin/token-budget.
|
|
72
|
+
node -e "const tb = require('./bin/token-budget.cjs'); const s = tb.getSessionStatus('.'); console.log(JSON.stringify(s));"
|
|
73
73
|
```
|
|
74
74
|
|
|
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.
|
|
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.cjs` (THRESHOLDS constant).
|
|
76
76
|
|
|
77
77
|
## Step 1: Load State (Lightweight)
|
|
78
78
|
|
|
@@ -116,7 +116,7 @@ From progress.md status, determine which phase to start from:
|
|
|
116
116
|
Before starting the phase loop, check the projected token cost for this milestone:
|
|
117
117
|
|
|
118
118
|
Run via Bash:
|
|
119
|
-
`node -e "const tb = require('./bin/token-budget.
|
|
119
|
+
`node -e "const tb = require('./bin/token-budget.cjs'); const est = tb.estimateMilestoneCost('.'); if(est) process.stdout.write(JSON.stringify(est));" 2>/dev/null`
|
|
120
120
|
|
|
121
121
|
If the command returns data, display to user:
|
|
122
122
|
- `estimated_tokens`: projected total tokens for this milestone
|
|
@@ -156,7 +156,7 @@ If STACK_RULES is empty (no templates/stacks/ dir or no matches), skip silently.
|
|
|
156
156
|
**Per-Phase Token Budget Check (before each phase spawn):**
|
|
157
157
|
|
|
158
158
|
Run via Bash:
|
|
159
|
-
`node -e "const tb = require('./bin/token-budget.
|
|
159
|
+
`node -e "const tb = require('./bin/token-budget.cjs'); const s = tb.getSessionStatus('.'); process.stdout.write(s.threshold);" 2>/dev/null`
|
|
160
160
|
|
|
161
161
|
Three-band model per `token-budget-contract.md` v3.0.0 (never silently degrade quality):
|
|
162
162
|
- `normal` or file missing → proceed normally
|
|
@@ -193,17 +193,18 @@ After phase agent returns — run via Bash:
|
|
|
193
193
|
Run via Bash AFTER each phase agent returns:
|
|
194
194
|
|
|
195
195
|
```bash
|
|
196
|
-
node -e "const tb=require('./bin/token-budget.
|
|
196
|
+
node -e "const tb=require('./bin/token-budget.cjs'); const s=tb.getSessionStatus('.'); process.stdout.write(JSON.stringify(s)); if(s.threshold==='stop'||s.threshold==='stale')process.exit(10); if(s.threshold==='warn')process.exit(13);"
|
|
197
197
|
```
|
|
198
198
|
|
|
199
199
|
The JSON on stdout contains `{consumed, estimated_remaining, pct, threshold}` — capture `pct` as `{CTX_PCT}` for the token-log row.
|
|
200
200
|
|
|
201
|
-
Exit-code handling (three-band model per `token-budget-contract.md` v3.0.0):
|
|
201
|
+
Exit-code handling (three-band model per `token-budget-contract.md` v3.0.0, extended with a fourth `stale` guard in v3.10.12):
|
|
202
202
|
- `0` (normal, <70%) → proceed to the next phase at full quality.
|
|
203
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
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.
|
|
205
|
+
- `10` (stale, meter dead) → **not resumable**. The context meter is dead (usually missing `ANTHROPIC_API_KEY`). Do NOT auto-spawn a fresh session — a fresh session would have the same broken guardrail. Instead, halt the wave and print `⚠ Context meter DEAD — run 'gsd-t doctor' and fix the cause before resuming`.
|
|
205
206
|
|
|
206
|
-
As of v2.0.0 (M34), the wave orchestrator reads the SAME `bin/token-budget.
|
|
207
|
+
As of v2.0.0 (M34), the wave orchestrator reads the SAME `bin/token-budget.cjs` 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.
|
|
207
208
|
|
|
208
209
|
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.
|
|
209
210
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tekyzinc/gsd-t",
|
|
3
|
-
"version": "3.10.
|
|
3
|
+
"version": "3.10.13",
|
|
4
4
|
"description": "GSD-T: Contract-Driven Development for Claude Code — 61 slash commands with unattended supervisor relay, headless CI/CD mode, graph-powered code analysis, real-time agent dashboard, execution intelligence, task telemetry, doc-ripple enforcement, backlog management, impact analysis, test sync, milestone archival, and PRD generation",
|
|
5
5
|
"author": "Tekyz, Inc.",
|
|
6
6
|
"license": "MIT",
|