@tekyzinc/gsd-t 4.9.14 → 4.10.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +1 -1
  3. package/bin/gsd-t-competition-judge.cjs +7 -1
  4. package/bin/gsd-t-context-brief-kinds/impact.cjs +91 -4
  5. package/bin/gsd-t-context-brief-kinds/partition.cjs +95 -0
  6. package/bin/gsd-t-context-brief-kinds/plan.cjs +110 -4
  7. package/bin/gsd-t-file-disjointness.cjs +319 -7
  8. package/bin/gsd-t-graph-anti-grep-lint.cjs +515 -0
  9. package/bin/gsd-t-graph-edge-extract.cjs +612 -0
  10. package/bin/gsd-t-graph-freshness.cjs +506 -0
  11. package/bin/gsd-t-graph-index.cjs +540 -0
  12. package/bin/gsd-t-graph-k1-sqlite-stream.cjs +251 -0
  13. package/bin/gsd-t-graph-query-cli.cjs +1182 -0
  14. package/bin/gsd-t-graph-scip-upgrade.cjs +440 -0
  15. package/bin/gsd-t-graph-store-bakeoff.cjs +889 -0
  16. package/bin/gsd-t-graph-synthetic-gen.cjs +304 -0
  17. package/bin/gsd-t-graph-ts-throughput.cjs +587 -0
  18. package/bin/gsd-t-scip-reader.cjs +167 -0
  19. package/bin/gsd-t.js +166 -48
  20. package/commands/gsd-t-debug.md +10 -0
  21. package/commands/gsd-t-design-build.md +10 -0
  22. package/commands/gsd-t-execute.md +15 -0
  23. package/commands/gsd-t-feature.md +15 -7
  24. package/commands/gsd-t-gap-analysis.md +17 -7
  25. package/commands/gsd-t-impact.md +25 -0
  26. package/commands/gsd-t-integrate.md +25 -0
  27. package/commands/gsd-t-partition.md +18 -0
  28. package/commands/gsd-t-plan.md +16 -0
  29. package/commands/gsd-t-populate.md +16 -5
  30. package/commands/gsd-t-prd.md +18 -0
  31. package/commands/gsd-t-project.md +19 -0
  32. package/commands/gsd-t-promote-debt.md +16 -5
  33. package/commands/gsd-t-qa.md +20 -8
  34. package/commands/gsd-t-quick.md +10 -0
  35. package/commands/gsd-t-scan.md +21 -3
  36. package/commands/gsd-t-test-sync.md +10 -0
  37. package/commands/gsd-t-verify.md +25 -0
  38. package/commands/gsd-t-wave.md +8 -0
  39. package/package.json +10 -2
  40. package/templates/workflows/gsd-t-debug.workflow.js +81 -0
  41. package/templates/workflows/gsd-t-integrate.workflow.js +47 -1
  42. package/templates/workflows/gsd-t-phase.workflow.js +84 -0
  43. package/templates/workflows/gsd-t-quick.workflow.js +64 -0
  44. package/templates/workflows/gsd-t-scan.workflow.js +200 -9
  45. package/templates/workflows/gsd-t-verify.workflow.js +50 -1
  46. package/bin/graph-cgc.js +0 -510
  47. package/bin/graph-indexer.js +0 -147
  48. package/bin/graph-overlay.js +0 -195
  49. package/bin/graph-parsers.js +0 -327
  50. package/bin/graph-query.js +0 -453
  51. package/bin/graph-store.js +0 -154
@@ -0,0 +1,304 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * gsd-t-graph-synthetic-gen — M94 D1-T1
5
+ *
6
+ * Synthetic ~1.5M-node graph generator for the K1 store bake-off spike.
7
+ * Produces a deterministic (seeded) node/edge set at configurable scale
8
+ * (default ~1.5M nodes, Atos scale hypothesis).
9
+ *
10
+ * Node types: FILE + FUNCTION/ENTITY nodes.
11
+ * Edge types: import (file→file) + call (function→function, funcId-keyed).
12
+ *
13
+ * Each node/edge carries the candidate schema fields:
14
+ * node: { id, kind, tier, contentHash, file, name? }
15
+ * edge: { kind, src, dst } — where src/dst are funcId / file paths
16
+ *
17
+ * Envelope emitted to stdout:
18
+ * { ok: true, nodes: [...], edges: [...], seed, scale, nodeCount, edgeCount }
19
+ * or { ok: false, error: "..." } on failure
20
+ *
21
+ * CLI:
22
+ * node bin/gsd-t-graph-synthetic-gen.cjs [--nodes N] [--seed S] [--out FILE]
23
+ * Flags:
24
+ * --nodes N total graph-node count (files + function/entity nodes), default 1500000
25
+ * --seed S integer seed for deterministic output, default 42
26
+ * --out FILE write envelope JSON to FILE instead of stdout
27
+ * --small alias for --nodes 10000 (fast test mode)
28
+ * --tiny alias for --nodes 500 (unit-test mode, returned in-memory)
29
+ *
30
+ * Exit: 0 on success, 1 on error.
31
+ * Zero external deps — Node built-ins only.
32
+ */
33
+
34
+ const fs = require("node:fs");
35
+ const path = require("node:path");
36
+ const crypto = require("node:crypto");
37
+
38
+ // ─── Seeded PRNG (xorshift32) ─────────────────────────────────────────────
39
+ // Reproducible across Node versions without external deps.
40
+ function makePrng(seed) {
41
+ let s = (seed >>> 0) || 1; // ensure non-zero uint32
42
+ return function () {
43
+ s ^= s << 13;
44
+ s ^= s >>> 17;
45
+ s ^= s << 5;
46
+ s = s >>> 0;
47
+ return s / 0x100000000;
48
+ };
49
+ }
50
+
51
+ // ─── Schema-field generators ──────────────────────────────────────────────
52
+ const TIERS = ["compiler-accurate", "tree-sitter-floor"];
53
+ const LANGS = ["ts", "js", "py", "rs"];
54
+ const EXT_MAP = { ts: ".ts", js: ".js", py: ".py", rs: ".rs" };
55
+ const KINDS_FILE = ["FILE"];
56
+ const KINDS_ENTITY = ["FUNCTION", "CLASS", "EXPORT"];
57
+
58
+ // Stable short hash placeholder (not crypto-strong — content-hash placeholder)
59
+ function syntheticHash(rng) {
60
+ // 8-char hex (deterministic from rng, not filesystem)
61
+ let h = "";
62
+ for (let i = 0; i < 8; i++) {
63
+ h += Math.floor(rng() * 16).toString(16);
64
+ }
65
+ return h;
66
+ }
67
+
68
+ // ─── Graph generation ─────────────────────────────────────────────────────
69
+
70
+ /**
71
+ * Generate a synthetic graph at `targetNodeCount` total nodes (FILE + FUNCTION/ENTITY).
72
+ *
73
+ * Atos-scale observation: real JS/TS repos have roughly 3–5 functions per file.
74
+ * We target FILE nodes = ~20% of total, ENTITY nodes = ~80%.
75
+ * Edge densities:
76
+ * import edges: ~3 per file node on average (fan-out import graph)
77
+ * call edges: ~4 per function entity on average (call graph)
78
+ *
79
+ * @param {number} targetNodeCount
80
+ * @param {number} seed
81
+ * @returns {{ nodes: object[], edges: object[], nodeCount: number, edgeCount: number }}
82
+ */
83
+ function generateGraph(targetNodeCount, seed) {
84
+ const rng = makePrng(seed);
85
+
86
+ // ── Node counts ──────────────────────────────────────────────────────────
87
+ const fileCount = Math.max(1, Math.round(targetNodeCount * 0.2));
88
+ const entityCount = Math.max(1, targetNodeCount - fileCount);
89
+
90
+ const nodes = [];
91
+ const edges = [];
92
+
93
+ // ── FILE nodes ───────────────────────────────────────────────────────────
94
+ // Generate file paths that look like a real repo
95
+ const filePaths = [];
96
+ const dirs = [
97
+ "src", "lib", "bin", "core", "utils", "api", "models", "controllers",
98
+ "services", "handlers", "parsers", "validators", "transforms", "types",
99
+ "helpers", "middleware", "routes", "adapters", "stores", "queries"
100
+ ];
101
+ const prefixes = [
102
+ "user", "auth", "index", "main", "core", "base", "common", "shared",
103
+ "graph", "cache", "config", "logger", "client", "server", "worker",
104
+ "loader", "parser", "builder", "factory", "manager"
105
+ ];
106
+
107
+ for (let i = 0; i < fileCount; i++) {
108
+ const lang = LANGS[Math.floor(rng() * LANGS.length)];
109
+ const ext = EXT_MAP[lang];
110
+ const dir = dirs[Math.floor(rng() * dirs.length)];
111
+ const prefix = prefixes[Math.floor(rng() * prefixes.length)];
112
+ const filePath = `${dir}/${prefix}-${i}${ext}`;
113
+ const contentHash = syntheticHash(rng);
114
+ const tier = TIERS[Math.floor(rng() * TIERS.length)];
115
+
116
+ filePaths.push(filePath);
117
+ nodes.push({
118
+ id: filePath,
119
+ kind: "FILE",
120
+ tier,
121
+ contentHash,
122
+ file: filePath,
123
+ });
124
+ }
125
+
126
+ // ── ENTITY nodes (functions / classes / exports) ─────────────────────────
127
+ // Each entity is file-qualified: funcId = "file#name" or "file#name@line"
128
+ // for disambiguation when multiple same-named entities exist per file.
129
+ // Entities are distributed across file nodes.
130
+ const entityNodes = [];
131
+ const funcIds = [];
132
+
133
+ // Distribution: every file gets at least 1 entity; remainder distributed randomly
134
+ const basePerFile = Math.max(1, Math.floor(entityCount / fileCount));
135
+ let entityIdx = 0;
136
+
137
+ for (let fi = 0; fi < filePaths.length && entityIdx < entityCount; fi++) {
138
+ const filePath = filePaths[fi];
139
+ // How many entities for this file?
140
+ const extras = fi === filePaths.length - 1
141
+ ? entityCount - entityIdx // last file gets all remaining
142
+ : Math.round(rng() * basePerFile * 1.5);
143
+ const count = Math.min(extras, entityCount - entityIdx);
144
+
145
+ const kindPool = KINDS_ENTITY;
146
+ const funcNamesUsed = new Set();
147
+
148
+ for (let ei = 0; ei < count && entityIdx < entityCount; ei++, entityIdx++) {
149
+ const kind = kindPool[Math.floor(rng() * kindPool.length)];
150
+ const baseName = `fn_${entityIdx}`;
151
+ // Make funcId file-qualified and unique within the file
152
+ const funcName = funcNamesUsed.has(baseName)
153
+ ? `${baseName}_${ei}`
154
+ : baseName;
155
+ funcNamesUsed.add(funcName);
156
+ const funcId = `${filePath}#${funcName}`;
157
+ const tier = TIERS[Math.floor(rng() * TIERS.length)];
158
+ const contentHash = syntheticHash(rng);
159
+
160
+ funcIds.push(funcId);
161
+ const node = {
162
+ id: funcId,
163
+ kind,
164
+ tier,
165
+ contentHash,
166
+ file: filePath,
167
+ name: funcName,
168
+ funcId,
169
+ };
170
+ entityNodes.push(node);
171
+ }
172
+ }
173
+
174
+ // NOTE: `nodes.push(...entityNodes)` overflows the call stack at ~870K-scale
175
+ // (array spread becomes function args, which has a ~65–120K limit). Use a
176
+ // for-loop append so the generator scales to the real Atos node count.
177
+ for (let i = 0; i < entityNodes.length; i++) nodes.push(entityNodes[i]);
178
+
179
+ // ── IMPORT edges (file→file) ─────────────────────────────────────────────
180
+ // Avg ~3 import edges per file node. Fan-out from each file to random other files.
181
+ // No self-loops.
182
+ const avgImports = 3;
183
+ for (let fi = 0; fi < filePaths.length; fi++) {
184
+ const src = filePaths[fi];
185
+ const count = Math.max(0, Math.round(rng() * avgImports * 2));
186
+ for (let k = 0; k < count; k++) {
187
+ let dstIdx = Math.floor(rng() * filePaths.length);
188
+ if (dstIdx === fi) dstIdx = (fi + 1) % filePaths.length;
189
+ edges.push({ kind: "IMPORT", src, dst: filePaths[dstIdx] });
190
+ }
191
+ }
192
+
193
+ // ── CALL edges (funcId→funcId) ───────────────────────────────────────────
194
+ // Avg ~4 call edges per entity node.
195
+ if (funcIds.length > 1) {
196
+ const avgCalls = 4;
197
+ for (let ei = 0; ei < funcIds.length; ei++) {
198
+ const caller = funcIds[ei];
199
+ const count = Math.max(0, Math.round(rng() * avgCalls * 2));
200
+ for (let k = 0; k < count; k++) {
201
+ let dstIdx = Math.floor(rng() * funcIds.length);
202
+ if (dstIdx === ei) dstIdx = (ei + 1) % funcIds.length;
203
+ edges.push({ kind: "CALL", src: caller, dst: funcIds[dstIdx] });
204
+ }
205
+ }
206
+ }
207
+
208
+ return {
209
+ nodes,
210
+ edges,
211
+ nodeCount: nodes.length,
212
+ edgeCount: edges.length,
213
+ };
214
+ }
215
+
216
+ // ─── CLI entry point ──────────────────────────────────────────────────────
217
+ function parseArgs(argv) {
218
+ const args = argv.slice(2);
219
+ let nodes = 1_500_000;
220
+ let seed = 42;
221
+ let out = null;
222
+ let i = 0;
223
+ while (i < args.length) {
224
+ const a = args[i];
225
+ if (a === "--nodes" && args[i + 1]) {
226
+ nodes = parseInt(args[++i], 10);
227
+ } else if (a === "--seed" && args[i + 1]) {
228
+ seed = parseInt(args[++i], 10);
229
+ } else if (a === "--out" && args[i + 1]) {
230
+ out = args[++i];
231
+ } else if (a === "--small") {
232
+ nodes = 10_000;
233
+ } else if (a === "--tiny") {
234
+ nodes = 500;
235
+ }
236
+ i++;
237
+ }
238
+ return { nodes, seed, out };
239
+ }
240
+
241
+ // Export for programmatic use (tests)
242
+ function generate(opts = {}) {
243
+ const nodes = opts.nodes ?? 500;
244
+ const seed = opts.seed ?? 42;
245
+ const result = generateGraph(nodes, seed);
246
+ return {
247
+ ok: true,
248
+ nodes: result.nodes,
249
+ edges: result.edges,
250
+ seed,
251
+ scale: nodes,
252
+ nodeCount: result.nodeCount,
253
+ edgeCount: result.edgeCount,
254
+ };
255
+ }
256
+
257
+ module.exports = { generate, generateGraph, makePrng };
258
+
259
+ // ─── CLI main ─────────────────────────────────────────────────────────────
260
+ if (require.main === module) {
261
+ const { nodes, seed, out } = parseArgs(process.argv);
262
+
263
+ let envelope;
264
+ try {
265
+ if (isNaN(nodes) || nodes < 1) {
266
+ throw new Error(`--nodes must be a positive integer, got: ${nodes}`);
267
+ }
268
+ if (isNaN(seed)) {
269
+ throw new Error(`--seed must be an integer, got: ${seed}`);
270
+ }
271
+ const result = generateGraph(nodes, seed);
272
+ envelope = {
273
+ ok: true,
274
+ nodes: result.nodes,
275
+ edges: result.edges,
276
+ seed,
277
+ scale: nodes,
278
+ nodeCount: result.nodeCount,
279
+ edgeCount: result.edgeCount,
280
+ };
281
+ } catch (err) {
282
+ envelope = { ok: false, error: err.message };
283
+ process.stdout.write(JSON.stringify(envelope) + "\n");
284
+ process.exit(1);
285
+ }
286
+
287
+ const json = JSON.stringify(envelope, null, 2);
288
+ if (out) {
289
+ fs.writeFileSync(out, json, "utf8");
290
+ // Summary to stdout when writing to file
291
+ const summary = {
292
+ ok: true,
293
+ seed: envelope.seed,
294
+ scale: envelope.scale,
295
+ nodeCount: envelope.nodeCount,
296
+ edgeCount: envelope.edgeCount,
297
+ writtenTo: out,
298
+ };
299
+ process.stdout.write(JSON.stringify(summary, null, 2) + "\n");
300
+ } else {
301
+ process.stdout.write(json + "\n");
302
+ }
303
+ process.exit(0);
304
+ }