@tekyzinc/gsd-t 2.39.13 → 2.46.11

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 (50) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/README.md +19 -10
  3. package/bin/desktop.ini +2 -0
  4. package/bin/global-sync-manager.js +350 -0
  5. package/bin/gsd-t.js +592 -2
  6. package/bin/metrics-collector.js +167 -0
  7. package/bin/metrics-rollup.js +200 -0
  8. package/bin/patch-lifecycle.js +195 -0
  9. package/bin/rule-engine.js +160 -0
  10. package/commands/desktop.ini +2 -0
  11. package/commands/gsd-t-complete-milestone.md +194 -6
  12. package/commands/gsd-t-debug.md +38 -3
  13. package/commands/gsd-t-doc-ripple.md +148 -0
  14. package/commands/gsd-t-execute.md +328 -54
  15. package/commands/gsd-t-help.md +32 -10
  16. package/commands/gsd-t-integrate.md +59 -7
  17. package/commands/gsd-t-metrics.md +143 -0
  18. package/commands/gsd-t-plan.md +49 -2
  19. package/commands/gsd-t-qa.md +26 -5
  20. package/commands/gsd-t-quick.md +36 -3
  21. package/commands/gsd-t-status.md +78 -0
  22. package/commands/gsd-t-test-sync.md +23 -2
  23. package/commands/gsd-t-verify.md +142 -10
  24. package/commands/gsd-t-visualize.md +11 -1
  25. package/commands/gsd-t-wave.md +64 -18
  26. package/docs/GSD-T-README.md +10 -6
  27. package/docs/architecture.md +84 -2
  28. package/docs/ci-examples/desktop.ini +2 -0
  29. package/docs/ci-examples/github-actions.yml +104 -0
  30. package/docs/ci-examples/gitlab-ci.yml +116 -0
  31. package/docs/desktop.ini +2 -0
  32. package/docs/framework-comparison-scorecard.md +160 -0
  33. package/docs/infrastructure.md +87 -1
  34. package/docs/prd-graph-engine.md +2 -2
  35. package/docs/prd-gsd2-hybrid.md +258 -135
  36. package/docs/requirements.md +66 -2
  37. package/examples/.gsd-t/contracts/desktop.ini +2 -0
  38. package/examples/.gsd-t/desktop.ini +2 -0
  39. package/examples/.gsd-t/domains/desktop.ini +2 -0
  40. package/examples/.gsd-t/domains/example-domain/desktop.ini +2 -0
  41. package/examples/desktop.ini +2 -0
  42. package/examples/rules/.gitkeep +0 -0
  43. package/examples/rules/desktop.ini +2 -0
  44. package/package.json +40 -40
  45. package/scripts/desktop.ini +2 -0
  46. package/scripts/gsd-t-dashboard-server.js +19 -2
  47. package/scripts/gsd-t-dashboard.html +63 -0
  48. package/scripts/gsd-t-event-writer.js +1 -0
  49. package/templates/CLAUDE-global.md +92 -10
  50. package/templates/desktop.ini +2 -0
@@ -0,0 +1,160 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * GSD-T Declarative Rule Engine — Pattern detection via JSONL rules
5
+ *
6
+ * Loads rules from .gsd-t/metrics/rules.jsonl, evaluates them against
7
+ * task-metrics data, manages activation tracking and rule lifecycle.
8
+ *
9
+ * Zero external dependencies (Node.js built-ins only).
10
+ */
11
+
12
+ const fs = require("fs");
13
+ const path = require("path");
14
+
15
+ module.exports = {
16
+ getActiveRules, evaluateRules, getPreMortemRules, getPatchTemplate,
17
+ recordActivation, flagInactiveRules, consolidateRules,
18
+ };
19
+
20
+ // ── getActiveRules ───────────────────────────────────────────────────────────
21
+
22
+ /** @param {string} [projectDir] @returns {object[]} Active rules */
23
+ function getActiveRules(projectDir) {
24
+ return loadJsonl(rulesPath(projectDir)).filter((r) => r.status === "active");
25
+ }
26
+
27
+ // ── evaluateRules ────────────────────────────────────────────────────────────
28
+
29
+ /** @param {string} domain @param {object} [opts] @returns {object[]} Matches */
30
+ function evaluateRules(domain, opts) {
31
+ const o = opts || {};
32
+ const dir = o.projectDir || process.cwd();
33
+ const rules = getActiveRules(dir);
34
+ const { readTaskMetrics } = require("./metrics-collector.js");
35
+ const filters = o.milestone ? { milestone: o.milestone } : {};
36
+ const allRecs = readTaskMetrics(filters, dir);
37
+ const domRecs = allRecs.filter((r) => r.domain === domain);
38
+ const matches = [];
39
+ for (const rule of rules) {
40
+ const win = rule.trigger.window || 0;
41
+ const scope = rule.trigger.scope || "domain";
42
+ let recs;
43
+ if (scope === "global") recs = win > 0 ? allRecs.slice(-win) : allRecs;
44
+ else if (scope === "milestone") {
45
+ recs = allRecs.filter((r) => r.milestone === o.milestone);
46
+ if (win > 0) recs = recs.slice(-win);
47
+ } else recs = win > 0 ? domRecs.slice(-win) : domRecs;
48
+ if (recs.length === 0) continue;
49
+ const matched = evalTrigger(rule.trigger, recs);
50
+ if (matched.length > 0) matches.push({ rule, matchedRecords: matched, severity: rule.severity });
51
+ }
52
+ return matches;
53
+ }
54
+
55
+ // ── getPreMortemRules ────────────────────────────────────────────────────────
56
+
57
+ /** @param {string} domainType @param {string} [projectDir] @returns {object[]} */
58
+ function getPreMortemRules(domainType, projectDir) {
59
+ return getActiveRules(projectDir || process.cwd()).filter((r) => r.activation_count > 0);
60
+ }
61
+
62
+ // ── getPatchTemplate ─────────────────────────────────────────────────────────
63
+
64
+ /** @param {string} templateId @param {string} [projectDir] @returns {object|null} */
65
+ function getPatchTemplate(templateId, projectDir) {
66
+ return loadJsonl(templatesPath(projectDir)).find((t) => t.id === templateId) || null;
67
+ }
68
+
69
+ // ── recordActivation ─────────────────────────────────────────────────────────
70
+
71
+ /** @param {string} ruleId @param {string} [projectDir] */
72
+ function recordActivation(ruleId, projectDir) {
73
+ const fp = rulesPath(projectDir);
74
+ const rules = loadJsonl(fp);
75
+ const r = rules.find((x) => x.id === ruleId);
76
+ if (!r) return;
77
+ r.activation_count = (r.activation_count || 0) + 1;
78
+ r.last_activated = new Date().toISOString();
79
+ atomicWriteJsonl(fp, rules);
80
+ }
81
+
82
+ // ── flagInactiveRules ────────────────────────────────────────────────────────
83
+
84
+ /** @param {number} threshold @param {string} [projectDir] @returns {object[]} */
85
+ function flagInactiveRules(threshold, projectDir) {
86
+ const rules = loadJsonl(rulesPath(projectDir));
87
+ return rules.filter((r) => {
88
+ if (r.status !== "active" || r.activation_count > 0) return false;
89
+ const num = parseMilestoneNum(r.milestone_created);
90
+ if (num === null) return false;
91
+ // Compare against highest milestone number among all rules as proxy for "current"
92
+ const maxM = rules.reduce((m, x) => Math.max(m, parseMilestoneNum(x.milestone_created) || 0), 0);
93
+ return (maxM - num) >= threshold;
94
+ });
95
+ }
96
+
97
+ // ── consolidateRules ─────────────────────────────────────────────────────────
98
+
99
+ /** @param {string[]} ruleIds @param {object} consolidated @param {string} [projectDir] */
100
+ function consolidateRules(ruleIds, consolidated, projectDir) {
101
+ const fp = rulesPath(projectDir);
102
+ const rules = loadJsonl(fp);
103
+ for (const rule of rules) { if (ruleIds.includes(rule.id)) rule.status = "consolidated"; }
104
+ rules.push(consolidated);
105
+ atomicWriteJsonl(fp, rules);
106
+ }
107
+
108
+ // ── Trigger evaluation ───────────────────────────────────────────────────────
109
+
110
+ function evalTrigger(trigger, records) {
111
+ const { metric, operator, threshold } = trigger;
112
+ if (operator === "pattern_count") {
113
+ const m = records.filter((r) => fieldVal(r, metric) != null);
114
+ return m.length >= threshold ? m : [];
115
+ }
116
+ return records.filter((r) => {
117
+ const v = fieldVal(r, metric);
118
+ return v != null && cmp(v, operator, threshold);
119
+ });
120
+ }
121
+
122
+ function cmp(v, op, t) {
123
+ switch (op) {
124
+ case "gt": return v > t; case "gte": return v >= t;
125
+ case "lt": return v < t; case "lte": return v <= t;
126
+ case "eq": return v === t; case "neq": return v !== t;
127
+ case "in": return Array.isArray(t) && t.includes(v);
128
+ default: return false;
129
+ }
130
+ }
131
+
132
+ function fieldVal(rec, metric) {
133
+ return metric === "first_pass_rate" ? (rec.pass ? 1 : 0) : rec[metric];
134
+ }
135
+
136
+ // ── Helpers ──────────────────────────────────────────────────────────────────
137
+
138
+ function rulesPath(d) { return path.join(d || process.cwd(), ".gsd-t", "metrics", "rules.jsonl"); }
139
+ function templatesPath(d) { return path.join(d || process.cwd(), ".gsd-t", "metrics", "patch-templates.jsonl"); }
140
+
141
+ function loadJsonl(fp) {
142
+ if (!fs.existsSync(fp)) return [];
143
+ const c = fs.readFileSync(fp, "utf8").trim();
144
+ return c ? c.split("\n").map(safeParse).filter(Boolean) : [];
145
+ }
146
+
147
+ function safeParse(l) { try { return JSON.parse(l); } catch { return null; } }
148
+
149
+ function atomicWriteJsonl(fp, records) {
150
+ const dir = path.dirname(fp);
151
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
152
+ const tmp = fp + ".tmp." + process.pid;
153
+ fs.writeFileSync(tmp, records.map((r) => JSON.stringify(r)).join("\n") + "\n");
154
+ fs.renameSync(tmp, fp);
155
+ }
156
+
157
+ function parseMilestoneNum(s) {
158
+ const m = (s || "").match(/M(\d+)/);
159
+ return m ? parseInt(m[1], 10) : null;
160
+ }
@@ -0,0 +1,2 @@
1
+ [.ShellClassInfo]
2
+ IconResource=C:\Program Files\Google\Drive File Stream\122.0.1.0\GoogleDriveFS.exe,27
@@ -3,7 +3,8 @@
3
3
  You are finalizing a completed milestone. Your job is to archive the milestone documentation, create a git tag, and prepare for the next milestone.
4
4
 
5
5
  This command is:
6
- - **Auto-invoked** at the end of `/user:gsd-t-wave` after verify passes
6
+ - **Auto-invoked** by `/user:gsd-t-verify` (Step 8) after all quality gates pass — at ALL autonomy levels
7
+ - **Auto-invoked** by `/user:gsd-t-wave` as part of the VERIFY+COMPLETE phase
7
8
  - **Standalone** when user wants to manually close a milestone
8
9
 
9
10
  ## Step 1: Verify Completion
@@ -50,6 +51,58 @@ Do not proceed to archiving. Create the smoke test now, run it, confirm it passe
50
51
 
51
52
  > This gate exists because complete-milestone is the last opportunity to catch "shipped blind" features before they become user-facing bugs requiring 15 debug sessions to resolve.
52
53
 
54
+ ## Step 1.75: Goal-Backward Verification Gate (MANDATORY)
55
+
56
+ Before archiving, verify that milestone goals are actually achieved end-to-end — not just structurally present. This catches placeholder implementations that passed all quality gates.
57
+
58
+ Refer to `.gsd-t/contracts/goal-backward-contract.md` for the full verification flow, placeholder patterns, and findings report format.
59
+
60
+ ### 1.75.1 Check for Existing Goal-Backward Results
61
+
62
+ 1. Read `.gsd-t/verify-report.md` — check if it contains a `Goal-Backward:` line
63
+ 2. If the verify-report already shows `Goal-Backward: PASS` or `Goal-Backward: WARN` (no CRITICAL/HIGH), skip to Step 2
64
+ 3. If no goal-backward results exist, or verify was run without this step, execute the full goal-backward check now (follow the same logic as `gsd-t-verify.md` Step 5.5)
65
+
66
+ ### 1.75.2 Evaluate Goal-Backward Status
67
+
68
+ **If CRITICAL or HIGH findings exist:**
69
+ - Display findings report to user in the contract format
70
+ - **BLOCK milestone completion** — do not proceed to archiving
71
+ - Prompt:
72
+ ```
73
+ ⛔ Goal-Backward Verification FAILED — milestone completion blocked.
74
+
75
+ Findings:
76
+ {findings table}
77
+
78
+ Options:
79
+ 1. Fix the findings and re-run /user:gsd-t-verify
80
+ 2. Override with explicit acknowledgment: re-run this command with --force-goal-backward
81
+
82
+ Proceed with option 1 (recommended) or acknowledge to force completion?
83
+ ```
84
+ - If user provides `--force-goal-backward` flag or explicit acknowledgment: log the override and proceed with warning
85
+ - Log to `.gsd-t/progress.md` Decision Log:
86
+ ```
87
+ - {date}: [goal-backward-override] Milestone "{name}" completed with unresolved goal-backward findings — user acknowledged. Findings: {summary}
88
+ ```
89
+
90
+ **If only MEDIUM findings (warnings):**
91
+ - Log findings to `.gsd-t/progress.md` Decision Log:
92
+ ```
93
+ - {date}: [goal-backward-warn] Goal-backward check found {N} medium findings before milestone archive — {summary}. Not blocking.
94
+ ```
95
+ - Proceed to Step 2
96
+
97
+ **If PASS (no findings):**
98
+ - Log to `.gsd-t/progress.md` Decision Log:
99
+ ```
100
+ - {date}: [goal-backward-pass] Goal-backward verification passed — {N} requirements checked, no placeholder patterns found
101
+ ```
102
+ - Proceed to Step 2
103
+
104
+ ---
105
+
53
106
  ## Step 2: Gap Analysis Gate
54
107
 
55
108
  After verification passes, run a gap analysis against `docs/requirements.md` scoped to this milestone's deliverables:
@@ -85,7 +138,112 @@ Before archiving, extract learning from the event stream to improve future runs.
85
138
  - If approved: append the rule to CLAUDE.md under the relevant section
86
139
  - Write event: `node ~/.claude/scripts/gsd-t-event-writer.js --type distillation --command gsd-t-complete-milestone --reasoning "{rule}" --outcome success || true`
87
140
 
88
- 5. If no patterns found (fewer than 3 occurrences): log "Distillation complete — no repeating patterns found", continue to Step 3
141
+ 5. If no patterns found (fewer than 3 occurrences): log "Distillation complete — no repeating patterns found"
142
+
143
+ ### Step 2.5b: Rule Engine Distillation
144
+
145
+ After event-stream pattern detection, run rule-based distillation using the declarative rule engine and patch lifecycle:
146
+
147
+ 1. **Rule Evaluation**: Run via Bash:
148
+ `node -e "const re = require('./bin/rule-engine.js'); const domains = [/* list milestone domain names */]; domains.forEach(d => { const m = re.evaluateRules(d, { projectDir: '.', milestone: '{milestone-id}' }); m.forEach(x => { console.log('FIRED: ' + x.rule.id + ' — ' + x.rule.name + ' [' + x.severity + ']'); re.recordActivation(x.rule.id, '.'); }); });" 2>/dev/null || true`
149
+
150
+ 2. **Patch Candidate Generation**: For each fired rule with `action: 'patch'`, run via Bash:
151
+ `node -e "const re = require('./bin/rule-engine.js'); const pl = require('./bin/patch-lifecycle.js'); const fired = [/* rule IDs from step 1 */]; fired.forEach(ruleId => { const rule = re.getActiveRules('.').find(r => r.id === ruleId); if(rule && rule.action === 'patch' && rule.patch_template_id) { const p = pl.createCandidate(ruleId, rule.patch_template_id, 0, '.'); console.log('CANDIDATE: ' + p.id + ' from ' + ruleId); } });" 2>/dev/null || true`
152
+
153
+ 3. **Promotion Gate Check**: For all applied/measured patches, run via Bash:
154
+ `node -e "const pl = require('./bin/patch-lifecycle.js'); ['applied','measured'].forEach(s => { pl.getPatchesByStatus(s, '.').forEach(p => { const g = pl.checkPromotionGate(p.id, '.'); if(g.passes) { pl.promote(p.id, '.'); console.log('PROMOTED: ' + p.id); } else if(p.measured_milestones && p.measured_milestones.length >= 2) { pl.deprecate(p.id, g.reason, '.'); console.log('DEPRECATED: ' + p.id + ' — ' + g.reason); } }); });" 2>/dev/null || true`
155
+
156
+ 4. **Graduation**: For promoted patches sustained 3+ milestones, run via Bash:
157
+ `node -e "const pl = require('./bin/patch-lifecycle.js'); pl.getPatchesByStatus('promoted', '.').forEach(p => { const r = pl.graduate(p.id, '.'); if(r.target) console.log('GRADUATED: ' + p.id + ' → ' + r.target); });" 2>/dev/null || true`
158
+
159
+ 5. **Consolidation & Deprecation**: Run via Bash:
160
+ `node -e "const re = require('./bin/rule-engine.js'); const f = re.flagInactiveRules(5, '.'); if(f.length) f.forEach(r => console.log('INACTIVE: ' + r.id + ' — no activations in 5+ milestones'));" 2>/dev/null || true`
161
+
162
+ 6. **Quality Budget Governance**: Compute rework percentage from task-metrics. Run via Bash:
163
+ `node -e "const mc = require('./bin/metrics-collector.js'); const recs = mc.readTaskMetrics({ milestone: '{milestone-id}' }, '.'); if(recs.length) { const rework = recs.filter(r => r.fix_cycles > 0).length; const pct = (rework/recs.length*100).toFixed(1); console.log('Rework: ' + pct + '% (' + rework + '/' + recs.length + ')'); if(pct > 20) console.log('⚠️ REWORK CEILING EXCEEDED — triggering constraint tightening for next milestone'); }" 2>/dev/null || true`
164
+
165
+ When rework ceiling (20%) exceeded: log warning and note that next milestone should: force discuss phase, require contract review, split large tasks.
166
+
167
+ ### Step 2.5c: Global Rule Promotion
168
+
169
+ After local rule promotion completes, propagate newly promoted rules to global metrics:
170
+
171
+ 1. **Check for promoted rules**: Run via Bash:
172
+ ```bash
173
+ node -e "const pl = require('./bin/patch-lifecycle.js'); const promoted = pl.getPatchesByStatus('promoted', '.'); console.log(JSON.stringify(promoted.map(p => ({ id: p.id, rule_id: p.rule_id, template_id: p.template_id }))));" 2>/dev/null || true
174
+ ```
175
+
176
+ 2. **Copy promoted rules to global**: For each promoted rule, run via Bash:
177
+ ```bash
178
+ node -e "
179
+ const gsm = require('./bin/global-sync-manager.js');
180
+ const re = require('./bin/rule-engine.js');
181
+ const pl = require('./bin/patch-lifecycle.js');
182
+ const promoted = pl.getPatchesByStatus('promoted', '.');
183
+ let count = 0;
184
+ for (const p of promoted) {
185
+ const rule = re.getActiveRules('.').find(r => r.id === p.rule_id);
186
+ if (!rule) continue;
187
+ const written = gsm.writeGlobalRule({
188
+ id: rule.id,
189
+ original_rule: rule,
190
+ source_project: (() => { try { return require('./package.json').name; } catch { return require('path').basename(process.cwd()); } })(),
191
+ source_project_dir: process.cwd(),
192
+ });
193
+ gsm.checkUniversalPromotion(written.global_id);
194
+ count++;
195
+ }
196
+ if (count > 0) console.log('Promoted ' + count + ' rules to global metrics');
197
+ " 2>/dev/null || true
198
+ ```
199
+
200
+ 3. **Write global rollup entry**: Run via Bash:
201
+ ```bash
202
+ node -e "
203
+ const gsm = require('./bin/global-sync-manager.js');
204
+ const mc = require('./bin/metrics-collector.js');
205
+ const mr = require('./bin/metrics-rollup.js');
206
+ const name = (() => { try { return require('./package.json').name; } catch { return require('path').basename(process.cwd()); } })();
207
+ const rollups = mr.readRollups({}, '.');
208
+ const latest = rollups.length > 0 ? rollups[rollups.length - 1] : null;
209
+ if (latest) {
210
+ gsm.writeGlobalRollup({
211
+ source_project: name, source_project_dir: process.cwd(),
212
+ milestone: latest.milestone, version: latest.version,
213
+ total_tasks: latest.total_tasks, first_pass_rate: latest.first_pass_rate,
214
+ avg_duration_s: latest.avg_duration_s, total_fix_cycles: latest.total_fix_cycles,
215
+ total_tokens: latest.total_tokens, elo_after: latest.elo_after,
216
+ signal_distribution: latest.signal_distribution,
217
+ domain_breakdown: latest.domain_breakdown,
218
+ });
219
+ console.log('Updated global rollup for ' + name);
220
+ }
221
+ " 2>/dev/null || true
222
+ ```
223
+
224
+ 4. **Write global signal distribution**: Run via Bash:
225
+ ```bash
226
+ node -e "
227
+ const gsm = require('./bin/global-sync-manager.js');
228
+ const mc = require('./bin/metrics-collector.js');
229
+ const name = (() => { try { return require('./package.json').name; } catch { return require('path').basename(process.cwd()); } })();
230
+ const allRecs = mc.readTaskMetrics({}, '.');
231
+ if (allRecs.length === 0) { process.exit(0); }
232
+ const counts = {};
233
+ allRecs.forEach(r => { counts[r.signal_type] = (counts[r.signal_type] || 0) + 1; });
234
+ const total = allRecs.length;
235
+ const rates = {};
236
+ for (const [k, v] of Object.entries(counts)) { rates[k] = Math.round(v / total * 1000) / 1000; }
237
+ gsm.writeGlobalSignalDistribution({
238
+ source_project: name, source_project_dir: process.cwd(),
239
+ total_tasks: total, signal_counts: counts, signal_rates: rates,
240
+ domain_type_signals: [],
241
+ });
242
+ console.log('Updated global signal distribution for ' + name);
243
+ " 2>/dev/null || true
244
+ ```
245
+
246
+ 5. If no rules were promoted this milestone: skip silently (steps 3-4 still run for rollup/signal tracking).
89
247
 
90
248
  ## Step 3: Gather Milestone Artifacts
91
249
 
@@ -233,6 +391,32 @@ If `.gsd-t/scan/` exists (a prior scan has been run):
233
391
 
234
392
  If `.gsd-t/scan/` doesn't exist → skip (no scan data to maintain).
235
393
 
394
+ ## Step 8.7: Generate Metrics Rollup
395
+
396
+ Generate milestone-level metrics aggregation and ELO score:
397
+
398
+ 1. Run via Bash:
399
+ `node bin/metrics-rollup.js {milestone-id} {version} 2>/dev/null`
400
+ 2. If rollup succeeds, display:
401
+ ```
402
+ ## Process Metrics — {milestone}
403
+ - ELO: {elo_before} → {elo_after} ({elo_delta > 0 ? '+' : ''}{elo_delta})
404
+ - First-pass rate: {first_pass_rate * 100}%
405
+ - Tasks: {total_tasks} | Fix cycles: {total_fix_cycles}
406
+ - Avg duration: {avg_duration_s}s | Avg context: {avg_context_pct}%
407
+ ```
408
+ 3. If `trend_delta` is present (previous milestone exists), display:
409
+ ```
410
+ Trend vs previous: first-pass rate {delta > 0 ? '↑' : '↓'} {delta}%, duration {delta}s
411
+ ```
412
+ 4. If `heuristic_flags` has entries, display as warnings:
413
+ ```
414
+ ⚠️ Heuristic: {heuristic} ({severity}) — {description}
415
+ ```
416
+ 5. If rollup fails (no task-metrics data): log "No task-metrics found — rollup skipped" and continue.
417
+
418
+ Include ELO score in the milestone summary (Step 5) and git tag message (Step 11).
419
+
236
420
  ## Step 9: Document Ripple
237
421
 
238
422
  Before creating the git tag, verify all documentation is up to date:
@@ -255,11 +439,15 @@ Before creating the git tag, verify all documentation is up to date:
255
439
 
256
440
  Verify the milestone is truly complete:
257
441
 
258
- 1. **Run the full test suite**: Execute ALL tests unit, integration, and E2E
259
- 2. **Run Playwright E2E** (if configured): Detect `playwright.config.*` or Playwright in dependencies. If present, run the full Playwright suite. If specs are missing or stale, invoke `gsd-t-test-sync` first.
260
- 3. **Verify all pass**: Every test must pass. If any fail, fix before tagging (up to 2 attempts)
442
+ 1. **Run ALL configured test suites** detect and run every one:
443
+ a. Unit/integration tests (vitest/jest/mocha)
444
+ b. If `playwright.config.*` exists run `npx playwright test` (full suite). Unit tests alone are NEVER sufficient when E2E exists.
445
+ c. If specs are missing or stale, invoke `gsd-t-test-sync` first.
446
+ d. Report: "Unit: X/Y pass | E2E: X/Y pass"
447
+ 2. **Verify all pass**: Every test must pass. If any fail, fix before tagging (up to 2 attempts)
448
+ 3. **Functional test quality gate**: Read every Playwright spec. Verify assertions check **functional behavior** (state changed after action, data loaded, content updated, widget responded to input) — NOT just element existence (`isVisible`, `toBeAttached`, `toBeEnabled`). Shallow tests that would pass on an empty HTML page with the right element IDs are a milestone completion FAIL. Flag and rewrite before proceeding.
261
449
  4. **Compare to baseline**: If a test baseline was recorded at milestone start, verify coverage has improved or at minimum not regressed
262
- 5. **Log test results**: Include test pass/fail counts in the milestone summary (Step 4)
450
+ 5. **Log test results**: Include test pass/fail counts and shallow test audit results in the milestone summary (Step 4)
263
451
 
264
452
  ## Step 11: Create Git Tag
265
453
 
@@ -281,13 +281,48 @@ Skip scan docs not affected by this fix. Skip analytical sections — those requ
281
281
  Before committing, ensure the fix is solid:
282
282
 
283
283
  1. **Update tests**: If the bug reveals a missing test case, add one that would have caught it
284
- 2. **Run affected tests**: Execute all tests related to the changed files and domain
284
+ 2. **Run ALL configured test suites** this is NOT optional:
285
+ a. Detect all runners: check for vitest/jest config, playwright.config.*, cypress.config.*
286
+ b. Run EVERY detected suite. Unit tests alone are NEVER sufficient when E2E exists.
287
+ c. If `playwright.config.*` exists → run `npx playwright test` (full suite)
288
+ d. Report ALL results: "Unit: X/Y pass | E2E: X/Y pass"
285
289
  3. **Verify passing**: All tests must pass. If any fail, fix before proceeding (up to 2 attempts)
286
- 4. **Run E2E tests**: If the fix changed UI, routes, or user flows and an E2E framework exists, run affected specs
287
- 5. **Regression check**: Confirm the fix doesn't break any adjacent functionality
290
+ 4. **If the project has a UI but no E2E specs cover the fixed area**: WRITE THEM.
291
+ 5. **Functional test quality**: Every E2E assertion must verify an action produced the correct outcome (state changed, data loaded, content updated) — not just that elements exist. Tests that only check `isVisible`/`toBeEnabled` are shallow layout tests and don't catch real bugs. If a test would pass on an empty HTML page with the right IDs, rewrite it.
292
+ 6. **Regression check**: Confirm the fix doesn't break any adjacent functionality
288
293
 
289
294
  Commit: `[debug] Fix {description} — root cause: {explanation}`
290
295
 
296
+ ## Step 5.5: Emit Task Metrics
297
+
298
+ After committing, emit a task-metrics record for this debug session — run via Bash:
299
+ `node bin/metrics-collector.js --milestone {current-milestone-or-none} --domain {domain-or-debug} --task debug-{timestamp} --command debug --duration_s {elapsed} --tokens_used {estimated} --context_pct ${CTX_PCT:-0} --pass {true|false} --fix_cycles {attempts} --signal_type debug-invoked --notes "[debug] {description}" 2>/dev/null || true`
300
+
301
+ Signal type is always `debug-invoked` for debug sessions.
302
+
303
+ Emit task_complete event — run via Bash:
304
+ `node ~/.claude/scripts/gsd-t-event-writer.js --type task_complete --command gsd-t-debug --reasoning "signal_type=debug-invoked, domain={domain}" --outcome {success|failure} || true`
305
+
306
+ ## Step 6: Doc-Ripple (Automated)
307
+
308
+ After all work is committed but before reporting completion:
309
+
310
+ 1. Run threshold check — read `git diff --name-only HEAD~1` and evaluate against doc-ripple-contract.md trigger conditions
311
+ 2. If SKIP: log "Doc-ripple: SKIP — {reason}" and proceed to completion
312
+ 3. If FIRE: spawn doc-ripple agent:
313
+
314
+ ⚙ [{model}] gsd-t-doc-ripple → blast radius analysis + parallel updates
315
+
316
+ Task subagent (general-purpose, model: sonnet):
317
+ "Execute the doc-ripple workflow per commands/gsd-t-doc-ripple.md.
318
+ Git diff context: {files changed list}
319
+ Command that triggered: debug
320
+ Produce manifest at .gsd-t/doc-ripple-manifest.md.
321
+ Update all affected documents.
322
+ Report: 'Doc-ripple: {N} checked, {N} updated, {N} skipped'"
323
+
324
+ 4. After doc-ripple returns, verify manifest exists and report summary inline
325
+
291
326
  $ARGUMENTS
292
327
 
293
328
  ## Auto-Clear
@@ -0,0 +1,148 @@
1
+ # GSD-T: Doc-Ripple — Automated Document Ripple Enforcement
2
+
3
+ You are the doc-ripple agent. You identify and update all downstream documents after code changes. You are spawned by execute, integrate, quick, debug, and wave after primary work is committed.
4
+
5
+ ## Step 1: Load Context
6
+
7
+ Read:
8
+ 1. `CLAUDE.md` — project conventions and Pre-Commit Gate (project-specific extensions)
9
+ 2. `.gsd-t/contracts/doc-ripple-contract.md` — trigger conditions, manifest format, update protocol
10
+ 3. `.gsd-t/contracts/pre-commit-gate.md` — the gate checklist you cross-reference
11
+
12
+ Run via Bash:
13
+ `git diff --name-only HEAD~1 2>/dev/null || git diff --cached --name-only`
14
+
15
+ Store the changed file list for Steps 2–3.
16
+
17
+ ## Step 2: Threshold Check
18
+
19
+ Evaluate the changed file list against trigger conditions from `doc-ripple-contract.md`.
20
+
21
+ Output exactly:
22
+ ```
23
+ DOC-RIPPLE THRESHOLD: {FIRE|SKIP}
24
+ Files changed: {N} across {N} directories
25
+ Cross-cutting signals: {list or "none"}
26
+ Reason: {brief explanation}
27
+ ```
28
+
29
+ **If SKIP**: log the decision, output `Doc-ripple: SKIP — {reason}`, and stop. No manifest. No updates.
30
+
31
+ **If FIRE**: proceed to Step 3.
32
+
33
+ ## Step 3: Blast Radius Analysis
34
+
35
+ For each changed file, classify its type: source, test, contract, template, command, doc, config.
36
+
37
+ Cross-reference `.gsd-t/contracts/pre-commit-gate.md` gate checklist:
38
+ - API endpoint/shape changed → api-contract.md, Swagger spec, CLAUDE.md, README.md
39
+ - Database schema changed → schema-contract.md, docs/schema.md
40
+ - UI component interface changed → component-contract.md
41
+ - New files or directories added → owning domain scope.md
42
+ - Requirement implemented or changed → docs/requirements.md
43
+ - Component or data flow changed → docs/architecture.md
44
+ - Any file modified → .gsd-t/progress.md (Decision Log entry)
45
+ - Architectural decision made → .gsd-t/progress.md (with rationale)
46
+ - Tech debt discovered or fixed → .gsd-t/techdebt.md
47
+ - New convention established → CLAUDE.md or domain constraints.md
48
+ - Command file added/changed → GSD-T-README.md, README.md, templates/CLAUDE-global.md, commands/gsd-t-help.md
49
+ - Command added/removed → all 4 above + package.json version + bin/gsd-t.js count
50
+ - Wave flow changed → gsd-t-wave.md, GSD-T-README.md, README.md
51
+ - Template changed → verify gsd-t-init output
52
+
53
+ Build the final list: `{ document, status (UPDATED|SKIPPED), action, reason }`.
54
+
55
+ ## Step 4: Generate Manifest
56
+
57
+ Write `.gsd-t/doc-ripple-manifest.md` (overwrite):
58
+
59
+ ```markdown
60
+ # Doc-Ripple Manifest — {date}
61
+
62
+ ## Trigger
63
+ - Command: {triggering command}
64
+ - Files changed: {N}
65
+ - Threshold: FIRE — {reason}
66
+
67
+ ## Blast Radius
68
+
69
+ | Document | Status | Action | Reason |
70
+ |----------|--------|--------|--------|
71
+ | {path} | {UPDATED|SKIPPED} | {action} | {reason} |
72
+
73
+ ## Summary
74
+ - Documents checked: {N}
75
+ - Documents updated: {N}
76
+ - Documents skipped (already current): {N}
77
+ ```
78
+
79
+ ## Step 5: Update Documents
80
+
81
+ Count documents marked UPDATED.
82
+
83
+ **Fewer than 3 updates — inline:**
84
+ For each document: read current content → determine minimal edit → apply via Edit tool (not Write) → verify after edit.
85
+
86
+ **3 or more updates — parallel subagents:**
87
+
88
+ For each document or logical group:
89
+
90
+ ⚙ [haiku] gsd-t-doc-ripple → update {document}
91
+ (Use sonnet for docs/architecture.md and docs/requirements.md — these need reasoning.)
92
+
93
+ **OBSERVABILITY LOGGING (MANDATORY) — for each subagent spawn:**
94
+
95
+ Before spawning — run via Bash:
96
+ `T_START=$(date +%s) && DT_START=$(date +"%Y-%m-%d %H:%M") && TOK_START=${CLAUDE_CONTEXT_TOKENS_USED:-0} && TOK_MAX=${CLAUDE_CONTEXT_TOKENS_MAX:-200000}`
97
+
98
+ After subagent returns — run via Bash:
99
+ `T_END=$(date +%s) && DT_END=$(date +"%Y-%m-%d %H:%M") && TOK_END=${CLAUDE_CONTEXT_TOKENS_USED:-0} && DURATION=$((T_END-T_START))`
100
+
101
+ Compute tokens:
102
+ - No compaction (TOK_END >= TOK_START): `TOKENS=$((TOK_END-TOK_START))`, COMPACTED=null
103
+ - Compaction detected (TOK_END < TOK_START): `TOKENS=$(((TOK_MAX-TOK_START)+TOK_END))`, COMPACTED=$DT_END
104
+
105
+ Compute context utilization:
106
+ `if [ "${CLAUDE_CONTEXT_TOKENS_MAX:-0}" -gt 0 ]; then CTX_PCT=$(echo "scale=1; ${CLAUDE_CONTEXT_TOKENS_USED:-0} * 100 / ${CLAUDE_CONTEXT_TOKENS_MAX}" | bc); else CTX_PCT="N/A"; fi`
107
+
108
+ Alert thresholds:
109
+ - CTX_PCT >= 85: `echo "🔴 CRITICAL: Context at ${CTX_PCT}% — compaction likely. Task MUST be split."`
110
+ - CTX_PCT >= 70: `echo "⚠️ WARNING: Context at ${CTX_PCT}% — approaching compaction threshold. Consider splitting."`
111
+
112
+ Append to `.gsd-t/token-log.md` (create with header `| Datetime-start | Datetime-end | Command | Step | Model | Duration(s) | Notes | Tokens | Compacted | Domain | Task | Ctx% |` if missing):
113
+ `| {DT_START} | {DT_END} | gsd-t-doc-ripple | Step 5 | {model} | {DURATION}s | update:{document} | {TOKENS} | {COMPACTED} | doc-ripple | — | {CTX_PCT} |`
114
+
115
+ **Each document-update subagent prompt:**
116
+ ```
117
+ Task subagent (general-purpose, model: {haiku|sonnet}):
118
+ "Update a single document as part of doc-ripple enforcement.
119
+
120
+ Document to update: {path}
121
+ Action: {action from manifest}
122
+ Reason: {reason from manifest}
123
+ Git diff context (changed files): {file list}
124
+
125
+ Instructions:
126
+ 1. Read the current document
127
+ 2. Apply the minimal edit — add/update only the affected section
128
+ 3. Use the Edit tool (not Write) — preserve all existing content
129
+ 4. Re-read after edit to confirm correctness
130
+ 5. Report: 'Updated {document} — {one-line summary of change}'"
131
+ ```
132
+
133
+ ## Step 6: Report Summary
134
+
135
+ Output:
136
+ ```
137
+ Doc-ripple: {N} checked, {N} updated, {N} skipped
138
+ ```
139
+
140
+ List each updated document with a one-line summary of what changed.
141
+
142
+ If any update failed, list it under `Failures:` and flag for manual review.
143
+
144
+ $ARGUMENTS
145
+
146
+ ## Auto-Clear
147
+
148
+ All work is written to project files. Execute `/clear` to free the context window for the next command.