@tekyzinc/gsd-t 3.23.11 → 3.25.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (74) hide show
  1. package/CHANGELOG.md +48 -0
  2. package/README.md +7 -0
  3. package/bin/cli-preflight-checks/branch-guard.cjs +110 -0
  4. package/bin/cli-preflight-checks/contracts-stable.cjs +128 -0
  5. package/bin/cli-preflight-checks/deps-installed.cjs +89 -0
  6. package/bin/cli-preflight-checks/manifest-fresh.cjs +98 -0
  7. package/bin/cli-preflight-checks/ports-free.cjs +110 -0
  8. package/bin/cli-preflight-checks/working-tree-state.cjs +149 -0
  9. package/bin/cli-preflight.cjs +265 -0
  10. package/bin/gsd-t-context-brief-kinds/design-verify.cjs +139 -0
  11. package/bin/gsd-t-context-brief-kinds/execute.cjs +205 -0
  12. package/bin/gsd-t-context-brief-kinds/qa.cjs +130 -0
  13. package/bin/gsd-t-context-brief-kinds/red-team.cjs +131 -0
  14. package/bin/gsd-t-context-brief-kinds/scan.cjs +118 -0
  15. package/bin/gsd-t-context-brief-kinds/verify.cjs +157 -0
  16. package/bin/gsd-t-context-brief.cjs +395 -0
  17. package/bin/gsd-t-ratelimit-probe-worker.cjs +236 -0
  18. package/bin/gsd-t-ratelimit-probe.cjs +648 -0
  19. package/bin/gsd-t-verify-gate-judge.cjs +224 -0
  20. package/bin/gsd-t-verify-gate.cjs +612 -0
  21. package/bin/gsd-t.js +45 -1
  22. package/bin/live-activity-report.cjs +615 -0
  23. package/bin/m55-substrate-proof.cjs +134 -0
  24. package/bin/parallel-cli-tee.cjs +206 -0
  25. package/bin/parallel-cli.cjs +478 -0
  26. package/commands/gsd-t-execute.md +31 -0
  27. package/commands/gsd-t-help.md +21 -0
  28. package/commands/gsd-t-verify.md +38 -0
  29. package/docs/architecture.md +194 -0
  30. package/docs/diagrams/.gsd-t/.context-meter-state.json +10 -0
  31. package/docs/diagrams/.gsd-t/context-meter.log +9 -0
  32. package/docs/diagrams/.gsd-t/events/2026-05-08.jsonl +45 -0
  33. package/docs/diagrams/.gsd-t/events/2026-05-09.jsonl +1 -0
  34. package/docs/diagrams/.gsd-t/heartbeat-cd9e7f59-ba5b-406a-9ed6-16762f039e81.jsonl +48 -0
  35. package/docs/diagrams/01-top-level-map-d2.png +0 -0
  36. package/docs/diagrams/01-top-level-map.d2 +77 -0
  37. package/docs/diagrams/01-top-level-map.mmd +48 -0
  38. package/docs/diagrams/01-top-level-map.png +0 -0
  39. package/docs/diagrams/01-top-level-map.svg +126 -0
  40. package/docs/diagrams/02-milestone-lifecycle-d2.png +0 -0
  41. package/docs/diagrams/02-milestone-lifecycle.d2 +38 -0
  42. package/docs/diagrams/02-milestone-lifecycle.mmd +36 -0
  43. package/docs/diagrams/02-milestone-lifecycle.png +0 -0
  44. package/docs/diagrams/02-milestone-lifecycle.svg +114 -0
  45. package/docs/diagrams/03-wave-mode-d2.png +0 -0
  46. package/docs/diagrams/03-wave-mode.d2 +33 -0
  47. package/docs/diagrams/03-wave-mode.mmd +21 -0
  48. package/docs/diagrams/03-wave-mode.png +0 -0
  49. package/docs/diagrams/03-wave-mode.svg +113 -0
  50. package/docs/diagrams/04-design-to-code-d2.png +0 -0
  51. package/docs/diagrams/04-design-to-code.d2 +35 -0
  52. package/docs/diagrams/04-design-to-code.mmd +29 -0
  53. package/docs/diagrams/04-design-to-code.png +0 -0
  54. package/docs/diagrams/04-design-to-code.svg +115 -0
  55. package/docs/diagrams/05-backlog-d2.png +0 -0
  56. package/docs/diagrams/05-backlog.d2 +40 -0
  57. package/docs/diagrams/05-backlog.mmd +20 -0
  58. package/docs/diagrams/05-backlog.png +0 -0
  59. package/docs/diagrams/05-backlog.svg +113 -0
  60. package/docs/diagrams/06-automation-utilities-d2.png +0 -0
  61. package/docs/diagrams/06-automation-utilities.d2 +48 -0
  62. package/docs/diagrams/06-automation-utilities.mmd +47 -0
  63. package/docs/diagrams/06-automation-utilities.png +0 -0
  64. package/docs/diagrams/06-automation-utilities.svg +110 -0
  65. package/docs/diagrams/_theme.d2 +86 -0
  66. package/docs/requirements.md +48 -0
  67. package/docs/workflow-diagram.md +338 -0
  68. package/package.json +1 -1
  69. package/scripts/gsd-t-dashboard-server.js +190 -0
  70. package/scripts/gsd-t-transcript.html +200 -0
  71. package/templates/CLAUDE-global.md +46 -0
  72. package/templates/prompts/design-verify-subagent.md +3 -0
  73. package/templates/prompts/qa-subagent.md +3 -0
  74. package/templates/prompts/red-team-subagent.md +3 -0
@@ -0,0 +1,134 @@
1
+ #!/usr/bin/env node
2
+ 'use strict';
3
+
4
+ /**
5
+ * M55-D2 Parallel-CLI Substrate Proof Harness (T5/T6)
6
+ *
7
+ * Charter SC2: ≥3× wall-clock speedup vs. serial baseline.
8
+ *
9
+ * Method:
10
+ * 1. Construct N synthetic CLI workers (default N=6), each `node -e
11
+ * 'setTimeout(_ => process.exit(0), 250)'` — deterministic 250 ms work
12
+ * with no real I/O. Mirrors `bin/m46-iter-proof.cjs` shape.
13
+ * 2. Sequential baseline: spawn each worker via `runParallel({maxConcurrency:1})`
14
+ * so the captureSpawn / tee path runs identically — only concurrency
15
+ * differs from the parallel run.
16
+ * 3. Parallel run: `runParallel({maxConcurrency:N})`. Same N workers.
17
+ * 4. Compute speedup = T_serial / T_par. Pass iff speedup ≥ 3.0.
18
+ *
19
+ * Output: stdout summary + appends to .gsd-t/metrics/m55-substrate-proof.txt.
20
+ * Exit: 0 on pass, 1 on fail.
21
+ */
22
+
23
+ const fs = require('fs');
24
+ const path = require('path');
25
+
26
+ const { runParallel } = require(path.join(__dirname, 'parallel-cli.cjs'));
27
+
28
+ const N_WORKERS = parseInt(process.env.M55_PROOF_N || '6', 10);
29
+ const SLEEP_MS = parseInt(process.env.M55_PROOF_SLEEP_MS || '250', 10);
30
+ const THRESHOLD_SPEEDUP = parseFloat(process.env.M55_PROOF_THRESHOLD || '3.0');
31
+
32
+ function makeWorkers(n, sleepMs) {
33
+ const workers = [];
34
+ for (let i = 0; i < n; i++) {
35
+ workers.push({
36
+ id: 'w' + String(i).padStart(2, '0'),
37
+ cmd: process.execPath,
38
+ args: ['-e', `setTimeout(() => process.exit(0), ${sleepMs})`],
39
+ });
40
+ }
41
+ return workers;
42
+ }
43
+
44
+ async function runSerial(n, sleepMs) {
45
+ const t0 = process.hrtime.bigint();
46
+ const r = await runParallel({
47
+ workers: makeWorkers(n, sleepMs),
48
+ maxConcurrency: 1,
49
+ command: 'm55-substrate-proof',
50
+ step: 'serial',
51
+ });
52
+ const t1 = process.hrtime.bigint();
53
+ return { wallClockMs: Number(t1 - t0) / 1e6, envelope: r };
54
+ }
55
+
56
+ async function runParallelN(n, sleepMs) {
57
+ const t0 = process.hrtime.bigint();
58
+ const r = await runParallel({
59
+ workers: makeWorkers(n, sleepMs),
60
+ maxConcurrency: n,
61
+ command: 'm55-substrate-proof',
62
+ step: 'parallel',
63
+ });
64
+ const t1 = process.hrtime.bigint();
65
+ return { wallClockMs: Number(t1 - t0) / 1e6, envelope: r };
66
+ }
67
+
68
+ async function main() {
69
+ process.stdout.write(
70
+ `M55-D2 Parallel-CLI Substrate Proof (N=${N_WORKERS} workers, sleep=${SLEEP_MS}ms each)\n`,
71
+ );
72
+ process.stdout.write('─'.repeat(60) + '\n');
73
+
74
+ process.stdout.write('Sequential baseline (maxConcurrency=1)…\n');
75
+ const serial = await runSerial(N_WORKERS, SLEEP_MS);
76
+ const serialOk = serial.envelope.results.filter((r) => r.ok).length;
77
+ process.stdout.write(` T_serial = ${serial.wallClockMs.toFixed(1)} ms (${serialOk}/${N_WORKERS} ok)\n\n`);
78
+
79
+ process.stdout.write(`Parallel (maxConcurrency=${N_WORKERS})…\n`);
80
+ const parallel = await runParallelN(N_WORKERS, SLEEP_MS);
81
+ const parallelOk = parallel.envelope.results.filter((r) => r.ok).length;
82
+ process.stdout.write(` T_par = ${parallel.wallClockMs.toFixed(1)} ms (${parallelOk}/${N_WORKERS} ok)\n\n`);
83
+
84
+ const speedup = serial.wallClockMs / parallel.wallClockMs;
85
+ const ratio = parallel.wallClockMs / serial.wallClockMs;
86
+ const parallelismFactor = (N_WORKERS * SLEEP_MS) / parallel.wallClockMs;
87
+ const passed = speedup >= THRESHOLD_SPEEDUP;
88
+
89
+ process.stdout.write('─'.repeat(60) + '\n');
90
+ process.stdout.write('Result\n');
91
+ process.stdout.write(` speedup = ${speedup.toFixed(2)}× (threshold ≥ ${THRESHOLD_SPEEDUP})\n`);
92
+ process.stdout.write(` T_par / T_serial = ${ratio.toFixed(3)}\n`);
93
+ process.stdout.write(` parallelism_factor = ${parallelismFactor.toFixed(2)} (ideal ≈ ${N_WORKERS})\n`);
94
+ process.stdout.write(` verdict = ${passed ? 'PASS ✓' : 'FAIL ✗'}\n`);
95
+
96
+ // Append result line to metrics file (charter SC2).
97
+ const metricsDir = path.join(process.cwd(), '.gsd-t', 'metrics');
98
+ fs.mkdirSync(metricsDir, { recursive: true });
99
+ const metricsPath = path.join(metricsDir, 'm55-substrate-proof.txt');
100
+ const summaryLine = [
101
+ new Date().toISOString(),
102
+ `N=${N_WORKERS}`,
103
+ `sleepMs=${SLEEP_MS}`,
104
+ `T_serial=${serial.wallClockMs.toFixed(1)}ms`,
105
+ `T_par=${parallel.wallClockMs.toFixed(1)}ms`,
106
+ `speedup=${speedup.toFixed(2)}x`,
107
+ `verdict=${passed ? 'PASS' : 'FAIL'}`,
108
+ ].join(' | ') + '\n';
109
+ fs.appendFileSync(metricsPath, summaryLine);
110
+ process.stdout.write(`\nAppended: ${metricsPath}\n`);
111
+
112
+ // Also dump a structured JSON sibling for downstream consumers.
113
+ const reportPath = path.join(metricsDir, 'm55-substrate-proof.json');
114
+ const report = {
115
+ timestamp: new Date().toISOString(),
116
+ n_workers: N_WORKERS,
117
+ sleep_ms: SLEEP_MS,
118
+ T_serial_ms: serial.wallClockMs,
119
+ T_par_ms: parallel.wallClockMs,
120
+ speedup,
121
+ parallelism_factor: parallelismFactor,
122
+ threshold_speedup: THRESHOLD_SPEEDUP,
123
+ passed,
124
+ };
125
+ fs.writeFileSync(reportPath, JSON.stringify(report, null, 2));
126
+ process.stdout.write(`Wrote: ${reportPath}\n`);
127
+
128
+ process.exit(passed ? 0 : 1);
129
+ }
130
+
131
+ main().catch((e) => {
132
+ process.stderr.write(`ERROR: ${(e && e.stack) || e}\n`);
133
+ process.exit(2);
134
+ });
@@ -0,0 +1,206 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * GSD-T parallel-cli tee helper (M55 D2)
5
+ *
6
+ * Streams stdout/stderr from a child process to NDJSON files under a tee
7
+ * directory, OR captures into a 1 MB-capped in-memory buffer that rotates
8
+ * to a tmp file on overflow.
9
+ *
10
+ * Contract: .gsd-t/contracts/parallel-cli-contract.md v1.0.0 § Tee Paths.
11
+ *
12
+ * Hard rules:
13
+ * 1. Zero external runtime deps.
14
+ * 2. NDJSON line shape: {"t": isoTs, "stream": "stdout"|"stderr", "data": line}.
15
+ * 3. Mid-stream binary fragments tolerated — bytes copied verbatim into `data`.
16
+ * 4. In-memory cap: 1 MB per stream. On overflow → rotate to os.tmpdir().
17
+ * 5. fs writes use append-mode + flushSync on close.
18
+ */
19
+
20
+ const fs = require('fs');
21
+ const os = require('os');
22
+ const path = require('path');
23
+
24
+ const IN_MEMORY_CAP_BYTES = 1024 * 1024;
25
+
26
+ const VALID_ID_RE = /^[A-Za-z0-9._-]+$/;
27
+
28
+ function _now() {
29
+ return new Date().toISOString();
30
+ }
31
+
32
+ function _ensureDir(dir) {
33
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
34
+ }
35
+
36
+ function _writeNdjsonLine(fd, streamName, data) {
37
+ const line = JSON.stringify({ t: _now(), stream: streamName, data }) + '\n';
38
+ fs.writeSync(fd, line);
39
+ }
40
+
41
+ /**
42
+ * Attach a tee to a child process's stdout + stderr.
43
+ *
44
+ * @param {object} child child_process.ChildProcess
45
+ * @param {object} opts
46
+ * @param {string} opts.workerId
47
+ * @param {string|null} opts.teeDir if null/undefined, in-memory mode
48
+ * @returns {{
49
+ * stdoutPath: string|null,
50
+ * stderrPath: string|null,
51
+ * stdoutBytes: () => number,
52
+ * stderrBytes: () => number,
53
+ * stdoutTruncatedToTemp: () => boolean,
54
+ * stderrTruncatedToTemp: () => boolean,
55
+ * close: () => Promise<void>,
56
+ * }}
57
+ */
58
+ function attachTee(child, opts) {
59
+ if (!opts || typeof opts.workerId !== 'string') {
60
+ throw new Error('attachTee: opts.workerId is required');
61
+ }
62
+ if (!VALID_ID_RE.test(opts.workerId)) {
63
+ throw new Error('attachTee: workerId contains illegal characters: ' + opts.workerId);
64
+ }
65
+
66
+ const teeDir = opts.teeDir || null;
67
+
68
+ if (teeDir) {
69
+ return _attachFileMode(child, opts.workerId, teeDir);
70
+ }
71
+ return _attachMemoryMode(child, opts.workerId);
72
+ }
73
+
74
+ // ── File-mode tee ───────────────────────────────────────────────────────────
75
+
76
+ function _attachFileMode(child, workerId, teeDir) {
77
+ _ensureDir(teeDir);
78
+ const stdoutPath = path.join(teeDir, workerId + '.stdout.ndjson');
79
+ const stderrPath = path.join(teeDir, workerId + '.stderr.ndjson');
80
+ const stdoutFd = fs.openSync(stdoutPath, 'a');
81
+ const stderrFd = fs.openSync(stderrPath, 'a');
82
+
83
+ const counters = { stdoutBytes: 0, stderrBytes: 0 };
84
+
85
+ function onChunk(streamName, fd, chunk) {
86
+ if (!chunk) return;
87
+ const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
88
+ counters[streamName + 'Bytes'] += buf.length;
89
+ // Split on newlines to keep NDJSON line-delimited even when multiple
90
+ // lines arrive in a single chunk; trailing partial line goes through as-is.
91
+ const text = buf.toString('utf8');
92
+ const lines = text.split(/\r?\n/);
93
+ for (let i = 0; i < lines.length; i++) {
94
+ const isLast = i === lines.length - 1;
95
+ const piece = lines[i];
96
+ if (isLast && piece === '') continue; // trailing newline handled
97
+ _writeNdjsonLine(fd, streamName, piece);
98
+ }
99
+ }
100
+
101
+ if (child.stdout) child.stdout.on('data', (c) => onChunk('stdout', stdoutFd, c));
102
+ if (child.stderr) child.stderr.on('data', (c) => onChunk('stderr', stderrFd, c));
103
+
104
+ let closed = false;
105
+ function close() {
106
+ if (closed) return Promise.resolve();
107
+ closed = true;
108
+ try { fs.fsyncSync(stdoutFd); } catch (_) { /* ignore */ }
109
+ try { fs.fsyncSync(stderrFd); } catch (_) { /* ignore */ }
110
+ try { fs.closeSync(stdoutFd); } catch (_) { /* ignore */ }
111
+ try { fs.closeSync(stderrFd); } catch (_) { /* ignore */ }
112
+ return Promise.resolve();
113
+ }
114
+
115
+ return {
116
+ stdoutPath,
117
+ stderrPath,
118
+ stdoutBytes: () => counters.stdoutBytes,
119
+ stderrBytes: () => counters.stderrBytes,
120
+ stdoutTruncatedToTemp: () => false,
121
+ stderrTruncatedToTemp: () => false,
122
+ close,
123
+ };
124
+ }
125
+
126
+ // ── Memory-mode tee ────────────────────────────────────────────────────────
127
+
128
+ function _attachMemoryMode(child, workerId) {
129
+ const buffers = {
130
+ stdout: { chunks: [], bytes: 0, rotatedPath: null },
131
+ stderr: { chunks: [], bytes: 0, rotatedPath: null },
132
+ };
133
+
134
+ function rotate(streamName) {
135
+ const slot = buffers[streamName];
136
+ if (slot.rotatedPath) return slot.rotatedPath;
137
+ const ts = Date.now();
138
+ const tmpPath = path.join(
139
+ os.tmpdir(),
140
+ 'parallel-cli-' + workerId + '-' + streamName + '-' + ts + '.tmp'
141
+ );
142
+ try {
143
+ const fd = fs.openSync(tmpPath, 'a');
144
+ for (const ch of slot.chunks) {
145
+ fs.writeSync(fd, ch);
146
+ }
147
+ fs.closeSync(fd);
148
+ slot.rotatedPath = tmpPath;
149
+ slot.chunks = []; // free memory
150
+ } catch (_) {
151
+ // best-effort; if rotate fails we keep the buffer
152
+ }
153
+ return slot.rotatedPath;
154
+ }
155
+
156
+ function appendToTemp(streamName, chunk) {
157
+ const slot = buffers[streamName];
158
+ try {
159
+ const fd = fs.openSync(slot.rotatedPath, 'a');
160
+ fs.writeSync(fd, chunk);
161
+ fs.closeSync(fd);
162
+ } catch (_) { /* best-effort */ }
163
+ }
164
+
165
+ function onChunk(streamName, chunk) {
166
+ if (!chunk) return;
167
+ const buf = Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk));
168
+ const slot = buffers[streamName];
169
+ slot.bytes += buf.length;
170
+ if (slot.rotatedPath) {
171
+ appendToTemp(streamName, buf);
172
+ return;
173
+ }
174
+ slot.chunks.push(buf);
175
+ if (slot.bytes >= IN_MEMORY_CAP_BYTES) {
176
+ rotate(streamName);
177
+ }
178
+ }
179
+
180
+ if (child.stdout) child.stdout.on('data', (c) => onChunk('stdout', c));
181
+ if (child.stderr) child.stderr.on('data', (c) => onChunk('stderr', c));
182
+
183
+ let closed = false;
184
+ function close() {
185
+ if (closed) return Promise.resolve();
186
+ closed = true;
187
+ return Promise.resolve();
188
+ }
189
+
190
+ return {
191
+ stdoutPath: null,
192
+ stderrPath: null,
193
+ stdoutBytes: () => buffers.stdout.bytes,
194
+ stderrBytes: () => buffers.stderr.bytes,
195
+ stdoutTruncatedToTemp: () => buffers.stdout.rotatedPath !== null,
196
+ stderrTruncatedToTemp: () => buffers.stderr.rotatedPath !== null,
197
+ rotatedPath: (streamName) => buffers[streamName] && buffers[streamName].rotatedPath,
198
+ close,
199
+ };
200
+ }
201
+
202
+ module.exports = {
203
+ attachTee,
204
+ IN_MEMORY_CAP_BYTES,
205
+ VALID_ID_RE,
206
+ };