@tekyzinc/gsd-t 3.29.11 → 4.0.14

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 (114) hide show
  1. package/CHANGELOG.md +171 -0
  2. package/bin/gsd-t-parallel.cjs +64 -13
  3. package/bin/gsd-t.js +48 -376
  4. package/bin/journey-coverage.cjs +6 -3
  5. package/bin/parallel-cli.cjs +13 -1
  6. package/commands/gsd-t-complete-milestone.md +6 -12
  7. package/commands/gsd-t-debug.md +47 -533
  8. package/commands/gsd-t-design-decompose.md +24 -497
  9. package/commands/gsd-t-doc-ripple.md +23 -139
  10. package/commands/gsd-t-execute.md +41 -958
  11. package/commands/gsd-t-feature.md +2 -6
  12. package/commands/gsd-t-gap-analysis.md +3 -6
  13. package/commands/gsd-t-help.md +2 -4
  14. package/commands/gsd-t-impact.md +26 -295
  15. package/commands/gsd-t-init-scan-setup.md +7 -7
  16. package/commands/gsd-t-integrate.md +44 -369
  17. package/commands/gsd-t-milestone.md +25 -124
  18. package/commands/gsd-t-partition.md +28 -539
  19. package/commands/gsd-t-plan.md +26 -472
  20. package/commands/gsd-t-prd.md +25 -311
  21. package/commands/gsd-t-quick.md +1 -1
  22. package/commands/gsd-t-resume.md +0 -33
  23. package/commands/gsd-t-scan.md +45 -652
  24. package/commands/gsd-t-verify.md +52 -569
  25. package/commands/gsd-t-wave.md +41 -430
  26. package/package.json +1 -1
  27. package/scripts/gsd-t-calibration-hook.js +3 -1
  28. package/scripts/hooks/pre-commit-journey-coverage +0 -2
  29. package/scripts/hooks/pre-commit-playwright-gate +0 -2
  30. package/templates/CLAUDE-global.md +44 -173
  31. package/templates/CLAUDE-project.md +118 -104
  32. package/templates/prompts/design-verify-subagent.md +3 -0
  33. package/templates/prompts/qa-subagent.md +3 -0
  34. package/templates/prompts/red-team-subagent.md +5 -2
  35. package/templates/workflows/_lib.js +176 -0
  36. package/templates/workflows/gsd-t-debug.workflow.js +86 -0
  37. package/templates/workflows/gsd-t-execute.workflow.js +206 -0
  38. package/templates/workflows/gsd-t-integrate.workflow.js +71 -0
  39. package/templates/workflows/gsd-t-phase.workflow.js +94 -0
  40. package/templates/workflows/gsd-t-quick.workflow.js +72 -0
  41. package/templates/workflows/gsd-t-scan.workflow.js +711 -0
  42. package/templates/workflows/gsd-t-verify.workflow.js +348 -0
  43. package/templates/workflows/gsd-t-wave.workflow.js +43 -0
  44. package/bin/check-headless-sessions.js +0 -140
  45. package/bin/context-budget-audit.cjs +0 -447
  46. package/bin/context-meter-config.cjs +0 -101
  47. package/bin/context-meter-config.test.cjs +0 -101
  48. package/bin/event-stream.cjs +0 -205
  49. package/bin/gsd-t-benchmark-orchestrator.js +0 -437
  50. package/bin/gsd-t-capture-lint.cjs +0 -440
  51. package/bin/gsd-t-economics.cjs +0 -315
  52. package/bin/gsd-t-in-session-usage.cjs +0 -213
  53. package/bin/gsd-t-orchestrator-config.cjs +0 -161
  54. package/bin/gsd-t-orchestrator-queue.cjs +0 -180
  55. package/bin/gsd-t-orchestrator-recover.cjs +0 -231
  56. package/bin/gsd-t-orchestrator-worker.cjs +0 -219
  57. package/bin/gsd-t-orchestrator.js +0 -535
  58. package/bin/gsd-t-parallel-probe.cjs +0 -132
  59. package/bin/gsd-t-ratelimit-probe-worker.cjs +0 -237
  60. package/bin/gsd-t-ratelimit-probe.cjs +0 -648
  61. package/bin/gsd-t-report-tokens.cjs +0 -549
  62. package/bin/gsd-t-stream-feed-client.cjs +0 -151
  63. package/bin/gsd-t-token-backfill.cjs +0 -366
  64. package/bin/gsd-t-token-capture.cjs +0 -321
  65. package/bin/gsd-t-token-dashboard.cjs +0 -353
  66. package/bin/gsd-t-token-regenerate-log.cjs +0 -129
  67. package/bin/gsd-t-tool-attribution.cjs +0 -377
  68. package/bin/gsd-t-tool-cost.cjs +0 -195
  69. package/bin/gsd-t-transcript-tee.cjs +0 -246
  70. package/bin/gsd-t-unattended-heartbeat.cjs +0 -188
  71. package/bin/gsd-t-unattended-platform.cjs +0 -551
  72. package/bin/gsd-t-unattended-platform.js +0 -381
  73. package/bin/gsd-t-unattended-safety.cjs +0 -773
  74. package/bin/gsd-t-unattended-safety.js +0 -788
  75. package/bin/gsd-t-unattended.cjs +0 -2109
  76. package/bin/gsd-t-unattended.js +0 -1367
  77. package/bin/gsd-t-worker-dispatch.cjs +0 -211
  78. package/bin/handoff-lock.cjs +0 -249
  79. package/bin/handoff-lock.js +0 -249
  80. package/bin/headless-auto-spawn.cjs +0 -711
  81. package/bin/headless-auto-spawn.js +0 -373
  82. package/bin/headless-exit-codes.cjs +0 -67
  83. package/bin/live-activity-report.cjs +0 -615
  84. package/bin/log-tail.cjs +0 -81
  85. package/bin/m44-proof-measure.cjs +0 -285
  86. package/bin/m46-iter-proof.cjs +0 -149
  87. package/bin/m46-worker-proof.cjs +0 -201
  88. package/bin/m55-substrate-proof.cjs +0 -134
  89. package/bin/metrics-rollup.js +0 -200
  90. package/bin/model-windows.cjs +0 -99
  91. package/bin/model-windows.test.cjs +0 -75
  92. package/bin/parallelism-report.cjs +0 -535
  93. package/bin/runway-estimator.cjs +0 -242
  94. package/bin/spawn-plan-derive.cjs +0 -163
  95. package/bin/spawn-plan-status-updater.cjs +0 -292
  96. package/bin/spawn-plan-writer.cjs +0 -204
  97. package/bin/supervisor-pid-fingerprint.cjs +0 -126
  98. package/bin/token-budget.cjs +0 -265
  99. package/bin/unattended-watch-format.cjs +0 -178
  100. package/bin/watch-progress.js +0 -155
  101. package/commands/gsd-t-unattended-stop.md +0 -85
  102. package/commands/gsd-t-unattended-watch.md +0 -465
  103. package/commands/gsd-t-unattended.md +0 -476
  104. package/commands/gsd-t-visualize.md +0 -116
  105. package/scripts/gsd-t-context-meter.e2e.test.js +0 -364
  106. package/scripts/gsd-t-context-meter.js +0 -341
  107. package/scripts/gsd-t-context-meter.test.js +0 -471
  108. package/scripts/gsd-t-dashboard-server.js +0 -1203
  109. package/scripts/gsd-t-post-commit-spawn-plan.sh +0 -86
  110. package/scripts/gsd-t-transcript.html +0 -1821
  111. package/scripts/hooks/gsd-t-conversation-capture.js +0 -439
  112. package/scripts/hooks/gsd-t-in-session-usage-hook.js +0 -84
  113. package/scripts/spawn-plan-fmt-tokens.cjs +0 -80
  114. package/templates/hooks/post-commit-spawn-plan.sh +0 -85
package/bin/gsd-t.js CHANGED
@@ -34,12 +34,6 @@ try {
34
34
  };
35
35
  }
36
36
 
37
- // Shared headless exit-code helper — lives in its own file so non-entry
38
- // modules (e.g. bin/gsd-t-unattended.cjs) can require it without pulling
39
- // in the full CLI. See commentary near the original declaration site and
40
- // in headless-exit-codes.cjs itself.
41
- const { mapHeadlessExitCode } = require(path.join(__dirname, "headless-exit-codes.cjs"));
42
-
43
37
  // ─── Configuration ───────────────────────────────────────────────────────────
44
38
 
45
39
  const CLAUDE_DIR = path.join(os.homedir(), ".claude");
@@ -1152,7 +1146,7 @@ function configureFigmaMcp() {
1152
1146
 
1153
1147
  // ─── Utility Scripts ─────────────────────────────────────────────────────────
1154
1148
 
1155
- const UTILITY_SCRIPTS = ["gsd-t-tools.js", "gsd-t-statusline.js", "gsd-t-event-writer.js", "gsd-t-dashboard-server.js", "gsd-t-dashboard.html", "gsd-t-transcript.html"];
1149
+ const UTILITY_SCRIPTS = ["gsd-t-tools.js", "gsd-t-statusline.js", "gsd-t-event-writer.js"];
1156
1150
 
1157
1151
  function installUtilityScripts() {
1158
1152
  ensureDir(SCRIPTS_DIR);
@@ -1760,17 +1754,11 @@ async function doInit(projectName) {
1760
1754
  initGsdtDir(projectDir, projectName, today);
1761
1755
  copyBinToolsToProject(projectDir, projectName);
1762
1756
 
1763
- // Context Meter: copy script + deps + config template, append .gitignore,
1764
- // register the global PostToolUse hook.
1765
- if (installContextMeter(projectDir)) {
1766
- success("Context meter installed (scripts/, config template, .gitignore)");
1767
- }
1768
- const cmHook = configureContextMeterHooks(SETTINGS_JSON);
1769
- if (cmHook.installed) {
1770
- if (cmHook.action === "added") success("Context meter PostToolUse hook added");
1771
- else if (cmHook.action === "updated") success("Context meter hook command refreshed");
1772
- else info("Context meter hook already configured");
1773
- }
1757
+ // M61 D1: Context Meter retired (token-budget.cjs + scripts/gsd-t-context-meter.js
1758
+ // deleted). Native /context replaces it. No longer provisioned at install —
1759
+ // installContextMeter / configureContextMeterHooks would register a PostToolUse
1760
+ // hook pointing at a deleted script. Both functions remain defined but unused
1761
+ // (cleaned in M65 follow-up).
1774
1762
 
1775
1763
  if (registerProject(projectDir)) success("Registered in ~/.claude/.gsd-t-projects");
1776
1764
 
@@ -2474,12 +2462,7 @@ function updateSingleProject(projectDir, counts) {
2474
2462
  // a new tool only requires appending to this array. Use .cjs extension so they
2475
2463
  // always run as CommonJS regardless of the project's package.json "type" field.
2476
2464
  const PROJECT_BIN_TOOLS = [
2477
- "archive-progress.cjs", "log-tail.cjs", "context-budget-audit.cjs",
2478
- "context-meter-config.cjs", "token-budget.cjs",
2479
- "gsd-t-token-capture.cjs",
2480
- "gsd-t-unattended.cjs", "gsd-t-unattended-platform.cjs", "gsd-t-unattended-safety.cjs",
2481
- "handoff-lock.cjs", "headless-auto-spawn.cjs",
2482
- "headless-exit-codes.cjs",
2465
+ "archive-progress.cjs",
2483
2466
  // M55 — preflight + parallel-cli + brief + verify-gate libraries copied to project bin/
2484
2467
  // so per-project workflows can `require('./bin/cli-preflight.cjs')` etc. without
2485
2468
  // depending on the globally-installed gsd-t CLI being on PATH.
@@ -3147,7 +3130,11 @@ async function doDoctor(opts) {
3147
3130
  issues += checkDoctorInstallation();
3148
3131
  issues += await checkDoctorProject(opts);
3149
3132
  issues += checkDoctorCgc();
3150
- issues += await checkDoctorContextMeter(process.cwd());
3133
+ // M61 D1: Context Meter retired (token-budget.cjs + context-meter hook deleted).
3134
+ // checkDoctorContextMeter would emit phantom errors for the deleted subsystem
3135
+ // and tell the user to reinstall it. Native /context replaces the meter.
3136
+ // M61 D4: dashboard orphan check left for the deleted dashboard-server is also
3137
+ // dead, but harmless (ps string-match, no require) — deferred.
3151
3138
  issues += checkDoctorDashboardOrphans(opts);
3152
3139
  // M50 D2: opt-in install of the playwright-gate pre-commit hook.
3153
3140
  if (opts && opts.installHooks) {
@@ -3508,11 +3495,43 @@ function buildHeadlessCmd(command, cmdArgs) {
3508
3495
  * Map claude output + process exit code to a GSD-T headless exit code.
3509
3496
  * Exit codes: 0=success, 1=verify-fail, 2=context-budget-exceeded, 3=error,
3510
3497
  * 4=blocked-needs-human, 5=command-dispatch-failed
3498
+ *
3499
+ * Inlined from the former bin/headless-exit-codes.cjs (M65) — that file existed
3500
+ * to decouple the exit-code map from the CLI so the unattended supervisor could
3501
+ * require it without pulling in gsd-t.js. The supervisor was retired in M61 D2,
3502
+ * so the only remaining requirer is this file; the helper now lives inline.
3503
+ *
3504
+ * Match terminal markers, not narration. A bare "tests failed" substring
3505
+ * appears in healthy output ("0 tests failed", "no tests failed", quoted in
3506
+ * prose). Require a non-zero count prefix or a structured terminal marker.
3507
+ * Bug history: M45 worker output contained "tests failed" 6× in narration,
3508
+ * mapping exit 0 → 1 and halting a successful run — keep these regexes anchored.
3511
3509
  */
3512
- // mapHeadlessExitCode now lives in ./headless-exit-codes.cjs — see re-export
3513
- // near the top of this file. Kept out-of-line here so non-entry modules
3514
- // (e.g. bin/gsd-t-unattended.cjs) can require the shared helper without
3515
- // pulling in the full CLI at bin/gsd-t.js.
3510
+ const HEADLESS_NONZERO_FAILURE_COUNT_RE =
3511
+ /(?:^|\b)([1-9]\d*)\s+(?:tests?|specs?|assertions?|examples?|suites?)\s+failed\b/i;
3512
+ const HEADLESS_STRUCTURED_FAIL_RE = /^FAIL[:\s]/m;
3513
+ const HEADLESS_JEST_SUMMARY_FAIL_RE = /^Tests:\s+\d+\s+failed/im;
3514
+ const HEADLESS_VERIFICATION_FAILED_RE =
3515
+ /(?:^|[.!?]\s+)(?:verification|verify|quality gate)\s+failed\b/im;
3516
+ const HEADLESS_CONTEXT_BUDGET_RE =
3517
+ /(?:^|[.!?]\s+)(?:context budget exceeded|context window exceeded|budget exceeded|token limit)\b/im;
3518
+ const HEADLESS_BLOCKED_HUMAN_RE =
3519
+ /\bblocked\b[\s\S]{0,80}?\b(?:needs? human|human input|human approval)\b/i;
3520
+
3521
+ function mapHeadlessExitCode(processExitCode, output) {
3522
+ if (processExitCode !== 0 && processExitCode !== null) return 3;
3523
+ const raw = output || "";
3524
+ if (/^unknown command:/im.test(raw)) return 5;
3525
+ if (HEADLESS_CONTEXT_BUDGET_RE.test(raw)) return 2;
3526
+ if (HEADLESS_BLOCKED_HUMAN_RE.test(raw)) return 4;
3527
+ if (
3528
+ HEADLESS_VERIFICATION_FAILED_RE.test(raw) ||
3529
+ HEADLESS_NONZERO_FAILURE_COUNT_RE.test(raw) ||
3530
+ HEADLESS_STRUCTURED_FAIL_RE.test(raw) ||
3531
+ HEADLESS_JEST_SUMMARY_FAIL_RE.test(raw)
3532
+ ) return 1;
3533
+ return 0;
3534
+ }
3516
3535
 
3517
3536
  /**
3518
3537
  * Generate a headless log file path.
@@ -4151,121 +4170,6 @@ function doMetrics(_args) {
4151
4170
  log(`${DIM}metrics removed in v3.12 — context meter is no longer telemetry-instrumented${RESET}`);
4152
4171
  }
4153
4172
 
4154
- function doStreamFeed(args) {
4155
- const sub = args[0];
4156
- const projectDir = process.cwd();
4157
- const pidFile = path.join(projectDir, ".gsd-t", "stream-feed", ".server.pid");
4158
- const portFile = path.join(projectDir, ".gsd-t", "stream-feed", ".server.port");
4159
-
4160
- function readPid() {
4161
- try { return parseInt(fs.readFileSync(pidFile, "utf8").trim(), 10); }
4162
- catch { return null; }
4163
- }
4164
- function readPort() {
4165
- try { return parseInt(fs.readFileSync(portFile, "utf8").trim(), 10); }
4166
- catch { return null; }
4167
- }
4168
- function isAlive(pid) {
4169
- if (!pid) return false;
4170
- try { process.kill(pid, 0); return true; } catch { return false; }
4171
- }
4172
-
4173
- if (!sub || sub === "help" || sub === "--help" || sub === "-h") {
4174
- log(`\n${BOLD}gsd-t stream-feed${RESET} — Localhost stream-json watcher (M40 D4)\n`);
4175
- log(`${BOLD}Usage:${RESET}`);
4176
- log(` gsd-t stream-feed ${CYAN}start${RESET} [--port N]`);
4177
- log(` gsd-t stream-feed ${CYAN}status${RESET}`);
4178
- log(` gsd-t stream-feed ${CYAN}stop${RESET}`);
4179
- log(`\nDefault port: 7842. Env override: GSD_T_STREAM_FEED_PORT.`);
4180
- return;
4181
- }
4182
-
4183
- if (sub === "start") {
4184
- const existing = readPid();
4185
- if (isAlive(existing)) {
4186
- log(`${YELLOW}stream-feed-server already running${RESET} (pid ${existing}, port ${readPort() || "?"})`);
4187
- return;
4188
- }
4189
- let port = null;
4190
- for (let i = 1; i < args.length; i++) {
4191
- if (args[i] === "--port") port = Number(args[++i]);
4192
- }
4193
- const { spawn } = require("child_process");
4194
- const serverScript = path.join(__dirname, "..", "scripts", "gsd-t-stream-feed-server.js");
4195
- const forward = ["--project-dir", projectDir];
4196
- if (port) forward.push("--port", String(port));
4197
- const feedDir = path.join(projectDir, ".gsd-t", "stream-feed");
4198
- try { fs.mkdirSync(feedDir, { recursive: true }); } catch { /* exists */ }
4199
- const out = fs.openSync(path.join(feedDir, "server.log"), "a");
4200
- const err = fs.openSync(path.join(feedDir, "server.log"), "a");
4201
- const child = spawn(process.execPath, [serverScript, ...forward], {
4202
- detached: true, stdio: ["ignore", out, err], cwd: projectDir,
4203
- });
4204
- child.unref();
4205
- try { fs.writeFileSync(pidFile, String(child.pid)); } catch { /* noop */ }
4206
- try { fs.writeFileSync(portFile, String(port || process.env.GSD_T_STREAM_FEED_PORT || 7842)); } catch { /* noop */ }
4207
- log(`${GREEN}✓${RESET} stream-feed-server started (pid ${child.pid}, port ${port || 7842})`);
4208
- log(` log: .gsd-t/stream-feed/server.log`);
4209
- return;
4210
- }
4211
-
4212
- if (sub === "status") {
4213
- const pid = readPid();
4214
- const port = readPort();
4215
- if (!isAlive(pid)) {
4216
- log(`${DIM}stream-feed-server not running${RESET}`);
4217
- return;
4218
- }
4219
- log(`${GREEN}●${RESET} stream-feed-server running`);
4220
- log(` pid: ${pid}`);
4221
- log(` port: ${port || 7842}`);
4222
- // Try to fetch live stats
4223
- try {
4224
- const http = require("http");
4225
- const done = { v: false };
4226
- const req = http.get({ host: "127.0.0.1", port: port || 7842, path: "/status", timeout: 1000 }, (res) => {
4227
- let body = "";
4228
- res.on("data", (c) => body += c);
4229
- res.on("end", () => {
4230
- try {
4231
- const j = JSON.parse(body);
4232
- log(` frames today: ${j.framesToday}`);
4233
- log(` clients: ${j.clients}`);
4234
- log(` stats: ingested=${j.stats.framesIngested} broadcast=${j.stats.framesBroadcast} kicked=${j.stats.kicked}`);
4235
- } catch { /* noop */ }
4236
- done.v = true;
4237
- });
4238
- });
4239
- req.on("error", () => { done.v = true; });
4240
- req.on("timeout", () => { req.destroy(); done.v = true; });
4241
- } catch { /* noop */ }
4242
- return;
4243
- }
4244
-
4245
- if (sub === "stop") {
4246
- const pid = readPid();
4247
- if (!isAlive(pid)) {
4248
- log(`${DIM}stream-feed-server not running${RESET}`);
4249
- try { fs.unlinkSync(pidFile); } catch { /* noop */ }
4250
- try { fs.unlinkSync(portFile); } catch { /* noop */ }
4251
- return;
4252
- }
4253
- try { process.kill(pid, "SIGTERM"); } catch { /* noop */ }
4254
- log(`${GREEN}✓${RESET} stream-feed-server stopped (pid ${pid})`);
4255
- // Clean up stale PID files after a short wait
4256
- setTimeout(() => {
4257
- if (!isAlive(pid)) {
4258
- try { fs.unlinkSync(pidFile); } catch { /* noop */ }
4259
- try { fs.unlinkSync(portFile); } catch { /* noop */ }
4260
- }
4261
- }, 500);
4262
- return;
4263
- }
4264
-
4265
- error(`Unknown stream-feed subcommand: ${sub}`);
4266
- log(`Try: gsd-t stream-feed --help`);
4267
- process.exit(1);
4268
- }
4269
4173
 
4270
4174
  function showHelp() {
4271
4175
  log(`\n${BOLD}GSD-T${RESET} — Contract-Driven Development for Claude Code\n`);
@@ -4282,12 +4186,7 @@ function showHelp() {
4282
4186
  log(` ${CYAN}changelog${RESET} Open changelog in the browser`);
4283
4187
  log(` ${CYAN}graph${RESET} Code graph operations (index, status, query)`);
4284
4188
  log(` ${CYAN}headless${RESET} Non-interactive execution via claude -p + fast state queries`);
4285
- log(` ${CYAN}orchestrate${RESET} External task orchestrator — one claude -p spawn per task (M40)`);
4286
- log(` ${CYAN}benchmark-orchestrator${RESET} M40 speed gate — compares orchestrator vs in-session wall-clock`);
4287
- log(` ${CYAN}stream-feed${RESET} Localhost stream-json watcher (start|status|stop) — M40 D4`);
4288
4189
  log(` ${CYAN}design-build${RESET} Deterministic design→code pipeline (elements → widgets → pages)`);
4289
- log(` ${CYAN}tool-cost${RESET} Per-tool token/cost attribution (M43 D2) — group-by tool|command|domain`);
4290
- log(` ${CYAN}report tokens${RESET} Generate token-usage optimization report (Run → Iter → CW → Turn → Tool)`);
4291
4190
  log(` ${CYAN}help${RESET} Show this help\n`);
4292
4191
  log(`${BOLD}Examples:${RESET}`);
4293
4192
  log(` ${DIM}$${RESET} npx @tekyzinc/gsd-t install`);
@@ -4495,22 +4394,6 @@ if (require.main === module) {
4495
4394
  case "headless":
4496
4395
  doHeadless(args.slice(1));
4497
4396
  break;
4498
- case "unattended": {
4499
- const { spawnSync } = require("child_process");
4500
- const cjs = path.join(__dirname, "gsd-t-unattended.cjs");
4501
- const res = spawnSync(process.execPath, [cjs, ...args.slice(1)], {
4502
- stdio: "inherit",
4503
- });
4504
- process.exit(res.status == null ? 1 : res.status);
4505
- }
4506
- case "orchestrate": {
4507
- const { spawnSync } = require("child_process");
4508
- const js = path.join(__dirname, "gsd-t-orchestrator.js");
4509
- const res = spawnSync(process.execPath, [js, ...args.slice(1)], {
4510
- stdio: "inherit",
4511
- });
4512
- process.exit(res.status == null ? 1 : res.status);
4513
- }
4514
4397
  case "parallel": {
4515
4398
  // M44 D2 — `gsd-t parallel` wraps M40 orchestrator with task-level
4516
4399
  // parallelism + mode-aware gating math. Extends, does not replace.
@@ -4518,14 +4401,6 @@ if (require.main === module) {
4518
4401
  const code = runParallelCli(args.slice(1), process.env);
4519
4402
  process.exit(code);
4520
4403
  }
4521
- case "benchmark-orchestrator": {
4522
- const { spawnSync } = require("child_process");
4523
- const js = path.join(__dirname, "gsd-t-benchmark-orchestrator.js");
4524
- const res = spawnSync(process.execPath, [js, ...args.slice(1)], {
4525
- stdio: "inherit",
4526
- });
4527
- process.exit(res.status == null ? 1 : res.status);
4528
- }
4529
4404
  case "preflight": {
4530
4405
  // M55 D5 — `gsd-t preflight` thin dispatcher to bin/cli-preflight.cjs.
4531
4406
  const { spawnSync } = require("child_process");
@@ -4589,212 +4464,9 @@ if (require.main === module) {
4589
4464
  });
4590
4465
  process.exit(res.status == null ? 1 : res.status);
4591
4466
  }
4592
- case "stream-feed": {
4593
- doStreamFeed(args.slice(1));
4594
- break;
4595
- }
4596
4467
  case "metrics":
4597
4468
  doMetrics(args.slice(1));
4598
4469
  break;
4599
- case "backfill-tokens": {
4600
- const bfOpts = { projectDir: process.cwd(), since: null, patchLog: false, dryRun: false };
4601
- for (let i = 1; i < args.length; i++) {
4602
- const a = args[i];
4603
- if (a === '--since' && args[i+1]) { bfOpts.since = args[++i]; }
4604
- else if (a.startsWith('--since=')) { bfOpts.since = a.slice(8); }
4605
- else if (a === '--patch-log') { bfOpts.patchLog = true; }
4606
- else if (a === '--dry-run') { bfOpts.dryRun = true; }
4607
- else if (a === '--project-dir' && args[i+1]) { bfOpts.projectDir = args[++i]; }
4608
- else if (a.startsWith('--project-dir=')) { bfOpts.projectDir = a.slice(14); }
4609
- else if (a === '--help' || a === '-h') {
4610
- log('Usage: gsd-t backfill-tokens [--since YYYY-MM-DD] [--patch-log] [--dry-run] [--project-dir PATH]');
4611
- process.exit(0);
4612
- }
4613
- else {
4614
- error(`backfill-tokens: unknown arg: ${a}`);
4615
- process.exit(2);
4616
- }
4617
- }
4618
- const backfill = require(path.join(__dirname, 'gsd-t-token-backfill.cjs'));
4619
- backfill.main(bfOpts)
4620
- .then(({ exitCode }) => process.exit(exitCode || 0))
4621
- .catch((e) => { error(e.message || String(e)); process.exit(3); });
4622
- break;
4623
- }
4624
- case "capture-lint": {
4625
- const clOpts = { projectDir: process.cwd(), mode: 'staged' };
4626
- let checkStreamJson = false;
4627
- for (let i = 1; i < args.length; i++) {
4628
- const a = args[i];
4629
- if (a === '--staged') { clOpts.mode = 'staged'; }
4630
- else if (a === '--all') { clOpts.mode = 'all'; }
4631
- else if (a === '--check-stream-json') { checkStreamJson = true; }
4632
- else if (a === '--project-dir' && args[i+1]) { clOpts.projectDir = args[++i]; }
4633
- else if (a.startsWith('--project-dir=')) { clOpts.projectDir = a.slice(14); }
4634
- else if (a === '--help' || a === '-h') {
4635
- log('Usage: gsd-t capture-lint [--staged] [--all] [--check-stream-json] [--project-dir PATH]');
4636
- log('');
4637
- log('Default mode (M41 D5): rejects bare Task() / claude -p / spawn() calls');
4638
- log(' missing the captureSpawn / recordSpawnRow wrapper.');
4639
- log('');
4640
- log('--check-stream-json (M56 D5): rejects claude -p / spawn(claude, ...) invocations');
4641
- log(' missing --output-format stream-json --verbose flag pair.');
4642
- log(' Allowlist via marker comment: GSD-T-LINT: skip stream-json (reason: …)');
4643
- process.exit(0);
4644
- }
4645
- else {
4646
- error(`capture-lint: unknown arg: ${a}`);
4647
- process.exit(2);
4648
- }
4649
- }
4650
- try {
4651
- const linter = require(path.join(__dirname, 'gsd-t-capture-lint.cjs'));
4652
- const res = checkStreamJson ? linter.mainStreamJson(clOpts) : linter.main(clOpts);
4653
- if (res.error) {
4654
- error(`capture-lint: ${res.error}`);
4655
- process.exit(2);
4656
- }
4657
- for (const v of res.violations) {
4658
- log(`${v.file}:${v.line}: ${v.message}`);
4659
- }
4660
- const modeLabel = checkStreamJson ? 'stream-json' : 'capture';
4661
- if (res.violations.length === 0) {
4662
- log(`${modeLabel}-lint: ${res.files.length} file(s) checked — clean`);
4663
- } else {
4664
- log(`${modeLabel}-lint: ${res.violations.length} violation(s) across ${res.files.length} file(s)`);
4665
- }
4666
- process.exit(res.exitCode);
4667
- } catch (e) {
4668
- error(e.message || String(e));
4669
- process.exit(2);
4670
- }
4671
- break;
4672
- }
4673
- case "tokens": {
4674
- const tkOpts = { projectDir: process.cwd(), since: null, milestone: null, format: 'table', regenerateLog: false, showToolCosts: false };
4675
- for (let i = 1; i < args.length; i++) {
4676
- const a = args[i];
4677
- if (a === '--since' && args[i+1]) { tkOpts.since = args[++i]; }
4678
- else if (a.startsWith('--since=')) { tkOpts.since = a.slice(8); }
4679
- else if (a === '--milestone' && args[i+1]) { tkOpts.milestone = args[++i]; }
4680
- else if (a.startsWith('--milestone=')) { tkOpts.milestone = a.slice(12); }
4681
- else if (a === '--format' && args[i+1]) { tkOpts.format = args[++i]; }
4682
- else if (a.startsWith('--format=')) { tkOpts.format = a.slice(9); }
4683
- else if (a === '--project-dir' && args[i+1]) { tkOpts.projectDir = args[++i]; }
4684
- else if (a.startsWith('--project-dir=')) { tkOpts.projectDir = a.slice(14); }
4685
- else if (a === '--regenerate-log') { tkOpts.regenerateLog = true; }
4686
- else if (a === '--show-tool-costs') { tkOpts.showToolCosts = true; }
4687
- else if (a === '--help' || a === '-h') {
4688
- log('Usage: gsd-t tokens [--since YYYY-MM-DD] [--milestone Mxx] [--format table|json] [--show-tool-costs]');
4689
- log(' gsd-t tokens --regenerate-log (rewrite .gsd-t/token-log.md from token-usage.jsonl)');
4690
- process.exit(0);
4691
- }
4692
- else {
4693
- error(`tokens: unknown arg: ${a}`);
4694
- process.exit(2);
4695
- }
4696
- }
4697
- if (tkOpts.regenerateLog) {
4698
- try {
4699
- const regen = require(path.join(__dirname, 'gsd-t-token-regenerate-log.cjs'));
4700
- const res = regen.regenerateLog({ projectDir: tkOpts.projectDir });
4701
- log(`Regenerated ${res.wrote} (${res.rowCount} row${res.rowCount === 1 ? '' : 's'})`);
4702
- process.exit(0);
4703
- } catch (e) {
4704
- error(e.message || String(e));
4705
- process.exit(3);
4706
- }
4707
- break;
4708
- }
4709
- if (tkOpts.format !== 'table' && tkOpts.format !== 'json') {
4710
- error(`tokens: --format must be 'table' or 'json' (got: ${tkOpts.format})`);
4711
- process.exit(2);
4712
- }
4713
- const dashboard = require(path.join(__dirname, 'gsd-t-token-dashboard.cjs'));
4714
- dashboard.aggregate(tkOpts)
4715
- .then((agg) => {
4716
- const baseOut = tkOpts.format === 'json' ? dashboard.renderJson(agg) : dashboard.renderTable(agg);
4717
- let out = baseOut;
4718
- if (tkOpts.showToolCosts) {
4719
- try {
4720
- out = out + '\n' + dashboard.renderToolCostsSection({
4721
- projectDir: tkOpts.projectDir,
4722
- since: tkOpts.since,
4723
- milestone: tkOpts.milestone,
4724
- format: tkOpts.format,
4725
- });
4726
- } catch (e) {
4727
- // Non-fatal: tool-cost section failure doesn't break primary output.
4728
- out = out + '\n── Top 10 tools by cost ──\n (tool-cost section unavailable: ' + (e.message || String(e)) + ')';
4729
- }
4730
- }
4731
- log(out);
4732
- process.exit(0);
4733
- })
4734
- .catch((e) => { error(e.message || String(e)); process.exit(3); });
4735
- break;
4736
- }
4737
- case "design-build": {
4738
- const orchestrator = require("./design-orchestrator.js");
4739
- orchestrator.run(args.slice(1)).catch(e => { console.error(e); process.exit(1); });
4740
- break;
4741
- }
4742
- case "tool-cost": {
4743
- const toolCost = require(path.join(__dirname, 'gsd-t-tool-cost.cjs'));
4744
- process.exit(toolCost.run(args.slice(1)));
4745
- break;
4746
- }
4747
- case "report": {
4748
- // Nested dispatch: `gsd-t report tokens [--date YYYY-MM-DD] [--out PATH]`
4749
- const sub = args[1];
4750
- if (!sub || sub === '--help' || sub === '-h') {
4751
- log('Usage: gsd-t report tokens [--date YYYY-MM-DD] [--out PATH]');
4752
- log('');
4753
- log('Subcommands:');
4754
- log(' tokens Generate token-usage optimization report (Run → Iter → CW → Turn → Tool).');
4755
- process.exit(sub ? 0 : 2);
4756
- break;
4757
- }
4758
- if (sub !== 'tokens') {
4759
- error(`report: unknown subcommand: ${sub}`);
4760
- log('Usage: gsd-t report tokens [--date YYYY-MM-DD] [--out PATH]');
4761
- process.exit(2);
4762
- break;
4763
- }
4764
- const rOpts = { projectDir: process.cwd(), date: null, outPath: null };
4765
- for (let i = 2; i < args.length; i++) {
4766
- const a = args[i];
4767
- if (a === '--date' && args[i+1]) { rOpts.date = args[++i]; }
4768
- else if (a.startsWith('--date=')) { rOpts.date = a.slice(7); }
4769
- else if (a === '--out' && args[i+1]) { rOpts.outPath = args[++i]; }
4770
- else if (a.startsWith('--out=')) { rOpts.outPath = a.slice(6); }
4771
- else if (a === '--project-dir' && args[i+1]) { rOpts.projectDir = args[++i]; }
4772
- else if (a.startsWith('--project-dir=')) { rOpts.projectDir = a.slice(14); }
4773
- else if (a === '--help' || a === '-h') {
4774
- log('Usage: gsd-t report tokens [--date YYYY-MM-DD] [--out PATH]');
4775
- log(' --date YYYY-MM-DD Date stamp in output filename (default: today UTC)');
4776
- log(' --out PATH Output path (default: .gsd-t/reports/token-usage-{DATE}.md)');
4777
- process.exit(0);
4778
- }
4779
- else {
4780
- error(`report tokens: unknown arg: ${a}`);
4781
- process.exit(2);
4782
- }
4783
- }
4784
- try {
4785
- const rep = require(path.join(__dirname, 'gsd-t-report-tokens.cjs'));
4786
- const res = rep.generateReport(rOpts);
4787
- log(`Wrote ${res.path}`);
4788
- const s = res.summary;
4789
- log(` ${s.cws} CW(s) · ${s.turns} turn rows · ${s.compactions} compactions · ${s.sessions} session(s)`);
4790
- log(` ${s.compactionEndedCWs}/${s.cws} CW${s.cws === 1 ? '' : 's'} ended by compaction · top tool: ${s.topTool || '—'}`);
4791
- process.exit(0);
4792
- } catch (e) {
4793
- error(e.message || String(e));
4794
- process.exit(3);
4795
- }
4796
- break;
4797
- }
4798
4470
  case "scan": {
4799
4471
  const exportFlag = args.find(a => a.startsWith('--export='));
4800
4472
  const exportFormat = exportFlag ? exportFlag.split('=')[1] : null;
@@ -3,10 +3,13 @@
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
5
 
6
+ // M61 D4 retired the specific viewer HTML + dashboard server. The detector's
7
+ // HTML-listener scanning stays general-purpose: any `scripts/*.html` is a
8
+ // viewer-source surface (so a future viewer is covered automatically), plus
9
+ // the live journey specs. The retired files simply no longer exist to scan —
10
+ // `gsd-t check-coverage` returns 0 listeners until a new HTML surface appears.
6
11
  const VIEWER_FILE_PATTERNS = [
7
- /^scripts\/gsd-t-transcript\.html$/,
8
- /^scripts\/gsd-t-dashboard-server\.js$/,
9
- /^bin\/gsd-t-dashboard.*\.cjs$/,
12
+ /^scripts\/.*\.html$/,
10
13
  /^e2e\/journeys\/.*\.spec\.ts$/,
11
14
  ];
12
15
 
@@ -26,7 +26,19 @@ const { spawn } = require('child_process');
26
26
  const fs = require('fs');
27
27
  const path = require('path');
28
28
 
29
- const { captureSpawn } = require('./gsd-t-token-capture.cjs');
29
+ // M61 D3: gsd-t-token-capture retired. captureSpawn was a wrapper that
30
+ // parsed result.usage and wrote a token-log row, returning
31
+ // `{ result, usage, rowWritten }`. Native /usage replaces the token-log.
32
+ // Stub preserves the RETURN SHAPE (callers read `wrapped.result`) — only the
33
+ // token-log side-effect is dropped. Returning the bare spawn result would
34
+ // make every parallel-CLI worker read `wrapped.result === undefined` and
35
+ // report ok:false regardless of real outcome (M61 post-audit CRITICAL fix).
36
+ const captureSpawn = async (opts) => {
37
+ if (typeof opts.spawnFn === 'function') {
38
+ return { result: await opts.spawnFn(), usage: null, rowWritten: false };
39
+ }
40
+ return { result: null, usage: null, rowWritten: false };
41
+ };
30
42
  const { attachTee, VALID_ID_RE } = require('./parallel-cli-tee.cjs');
31
43
 
32
44
  const SCHEMA_VERSION = '1.0.0';
@@ -463,19 +463,13 @@ node scripts/gsd-t-watch-state.js advance --agent-id "$GSD_T_AGENT_ID" --parent-
463
463
 
464
464
  Before tagging, ensure scan docs reflect the final state of the codebase after all milestone work. This is the full-refresh counterpart to the micro-updates done during execute/quick/debug.
465
465
 
466
- If `.gsd-t/scan/` exists (a prior scan has been run):
467
- 1. Check `.gsd-t/scan/.cache.json` for staleness count commits since last scan
468
- 2. If **any dimension is stale** (>0 commits since scan):
469
- - Log: "Running milestone scan checkpoint — refreshing all stale scan dimensions..."
470
- - Re-run all stale dimensions using scan teammates (same team structure as `gsd-t-scan` Step 2):
471
- - Stale `architecture.md` → architecture teammate (model: haiku)
472
- - Stale `quality.md` → quality teammate (model: sonnet)
473
- - Stale `security.md` → security teammate (model: sonnet)
474
- - Stale `business-rules.md` → business-rules teammate (model: haiku)
475
- - Stale `contract-drift.md` → contracts teammate (model: haiku)
476
- - Update `.gsd-t/scan/.cache.json` after refresh
466
+ If `.gsd-t/techdebt.md` exists (a prior scan has been run):
467
+ 1. Check staleness — count commits since the register was last written (use `.gsd-t/scan/.cache.json` if present, else the register's git mtime).
468
+ 2. If **stale** (>0 commits since scan):
469
+ - Log: "Running milestone scan checkpoint — refreshing the tech-debt register..."
470
+ - Re-run the scan by invoking the volume-scaled scan Workflow (`templates/workflows/gsd-t-scan.workflow.js`, same as `/gsd-t-scan`) the probe re-slices the codebase and the finders refresh the register. This replaces the retired fixed-5-teammate dimension refresh.
477
471
  - Update `.gsd-t/techdebt.md` — mark any items resolved during this milestone as `[RESOLVED]`
478
- 3. If all dimensions are fresh → skip with log: "Scan docs are fresh — no checkpoint refresh needed"
472
+ 3. If fresh → skip with log: "Scan register is fresh — no checkpoint refresh needed"
479
473
 
480
474
  If `.gsd-t/scan/` doesn't exist → skip (no scan data to maintain).
481
475