@tekyzinc/gsd-t 4.10.10 → 4.10.12

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -2,6 +2,32 @@
2
2
 
3
3
  All notable changes to GSD-T are documented here. Updated with each release.
4
4
 
5
+ ## [4.10.12] - 2026-06-27
6
+
7
+ ### Fixed — freshness hash mismatch made every graph query re-index the whole repo
8
+
9
+ `compute_touched_files` hashed files with md5 (full length) while the indexer stores sha256 sliced to 16 chars — a guaranteed permanent mismatch. So every file read as "edited" on every query and freshness re-indexed the entire repo (re-running SCIP), hanging `gsd-t graph status` for 30s+ (then reporting graph-unavailable) on large projects.
10
+
11
+ - `bin/gsd-t-graph-freshness.cjs`: `hashFileContent` now `sha256().slice(0,16)` — matches the indexer's `contentHash` exactly. `EXCLUDE_DIRS` synced with the indexer's `SKIP_DIRS` (`.venv`/`site-packages`/etc.) so freshness doesn't see phantom ADDs from vendored dirs the indexer skipped.
12
+ - 5 D4 freshness test fixtures seeded stores with md5 hashes — updated to sha256(16) to match the real store.
13
+
14
+ Measured on Tekyz-CRM: `gsd-t graph status` 30s-timeout-unavailable → 0.38s ok; `compute_touched_files` 291ms / 11,821-files-stale → 12ms / 0-stale. This also makes graph queries cheap enough for ambient grep-interception (the next milestone). Suite: 2502/2502 pass.
15
+
16
+ ## [4.10.11] - 2026-06-27
17
+
18
+ ### Fixed — M96: the code graph now actually runs in projects (native-dep resolution)
19
+
20
+ M94/M95 copied the graph runtime into every project's `bin/`, but it couldn't run: the native engines (better-sqlite3 store + tree-sitter floor parsers) were `devDependencies`, and a copied tool resolved them from the *project's* node_modules — which lacked them. The result was a SILENT empty graph (0 nodes/edges that read as a successful build). M96 makes the graph runnable everywhere.
21
+
22
+ - `package.json`: moved `better-sqlite3` + `tree-sitter` + `tree-sitter-typescript` + `tree-sitter-python` from devDependencies to `dependencies` (they ship now).
23
+ - `bin/gsd-t-require-store.cjs`: NEW `requireGraphDep()` — resolves a native dep from project node_modules → GSD-T global package node_modules → GSD-T dev tree; FAIL LOUD with a remediation message if all miss.
24
+ - `bin/gsd-t-graph-edge-extract.cjs`: resolves tree-sitter via the resolver and THROWS (not silent-0) when the mandatory floor parser is absent.
25
+ - `bin/gsd-t-graph-{index,freshness,query-cli}.cjs`: store engine via the resolver.
26
+ - `bin/gsd-t.js`: propagate `gsd-t-require-store.cjs` to projects.
27
+ - `test/m50-d2-viewer-specs-smoke.test.js`: zero-dep invariant updated to allow ONLY the graph native engines as runtime deps.
28
+
29
+ Proven on binvoice (a real non-GSD-T project): graph builds 471 files / 43,309 edges; `blast-radius(src/build-config.ts)` returns its real dependents (the consumer reads the graph, not grep). Decision reaffirmed: zero-dependency is a guiding principle, not a hard rule — native deps are justified because the graph cannot function without them. Suite: 2502/2502 pass.
30
+
5
31
  ## [4.10.10] - 2026-06-26
6
32
 
7
33
  ### Added — M94 persistent code graph + M95 real SCIP call-graph resolution; graph runtime now propagates into projects
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # GSD-T: Contract-Driven Development for Claude Code
2
2
 
3
- **v4.10.10** - A methodology for reliable, parallelizable development using Claude Code with optional Agent Teams support.
3
+ **v4.10.12** - A methodology for reliable, parallelizable development using Claude Code with optional Agent Teams support.
4
4
 
5
5
  **Eliminates context rot** — task-level fresh dispatch (one subagent per task, ~10-20% context each) means compaction never triggers.
6
6
  **Compaction-proof debug loops** — `gsd-t headless --debug-loop` runs test-fix-retest cycles as separate `claude -p` sessions. A JSONL debug ledger persists all hypothesis/fix/learning history across fresh sessions. Anti-repetition preamble injection prevents retrying failed hypotheses. Escalation tiers (sonnet → opus → human) and a hard iteration ceiling enforced externally.
@@ -63,19 +63,29 @@ let pythonAvailable = false;
63
63
  function ensureParsers() {
64
64
  if (_parsersLoaded) return;
65
65
  _parsersLoaded = true;
66
+ // M96: resolve the tree-sitter native modules via the multi-location resolver so
67
+ // a COPIED extractor (in a project's bin/) finds them in the GSD-T global package,
68
+ // not the project's own (absent) node_modules. The TS grammar is the MANDATORY
69
+ // floor parser — if it cannot load, FAIL LOUD. A silent fall-through to
70
+ // tsAvailable=false produced an empty graph (0 nodes/edges) that looked like a
71
+ // successful build — the exact silent-degrade this milestone exists to kill.
72
+ const { requireGraphDep } = require('./gsd-t-require-store.cjs');
66
73
  try {
67
- Parser = require('tree-sitter');
68
- TSGrammars = require('tree-sitter-typescript');
74
+ Parser = requireGraphDep('tree-sitter');
75
+ TSGrammars = requireGraphDep('tree-sitter-typescript');
69
76
  TSX = TSGrammars.tsx;
70
77
  tsAvailable = true;
71
78
  } catch (e) {
72
- warn(`tree-sitter-typescript not available: ${e.message}`);
79
+ throw new Error(
80
+ `code-graph floor parser unavailable: ${e.message} — the graph cannot be built ` +
81
+ `without tree-sitter. Reinstall GSD-T (npx @tekyzinc/gsd-t install).`
82
+ );
73
83
  }
74
84
  try {
75
- Python = require('tree-sitter-python');
85
+ Python = requireGraphDep('tree-sitter-python');
76
86
  pythonAvailable = true;
77
87
  } catch {
78
- /* Python optional */
88
+ /* Python optional — TS/JS still index */
79
89
  }
80
90
  }
81
91
 
@@ -48,22 +48,32 @@ function fail(msg) { return `${C.red}✘${C.reset} ${msg}`; }
48
48
  // ─── Source-file extensions the indexer tracks ────────────────────────────────
49
49
  const TRACKED_EXTS = new Set(['.js', '.mjs', '.cjs', '.ts', '.tsx', '.jsx', '.py']);
50
50
 
51
- // ─── Content-hash (SHA-256 hex, first 16 bytes 32 hex chars) ───────────────
52
- // Using MD5 for speed (same family as graph-store.js) collision-resistance
53
- // is not required here; we only need change-detection fidelity.
51
+ // ─── Content-hash MUST match the indexer's contentHash EXACTLY ─────────────
52
+ // The indexer (gsd-t-graph-index.cjs::contentHash) stores sha256(content) sliced
53
+ // to the first 16 hex chars. Freshness MUST compute the identical value, or EVERY
54
+ // file reads as "edited" on every query (the hash never matches) and freshness
55
+ // re-indexes the whole repo — the hang. (Was md5/full-length: a guaranteed
56
+ // permanent mismatch.) [RULE] freshness-hash-matches-indexer-hash
54
57
  function hashFileContent(filePath) {
55
58
  try {
56
59
  const content = fs.readFileSync(filePath);
57
- return crypto.createHash('md5').update(content).digest('hex');
60
+ return crypto.createHash('sha256').update(content).digest('hex').slice(0, 16);
58
61
  } catch {
59
62
  return null;
60
63
  }
61
64
  }
62
65
 
63
66
  // ─── Walk the working tree for source files ───────────────────────────────────
67
+ // MUST stay in sync with the indexer's SKIP_DIRS (gsd-t-graph-index.cjs). If
68
+ // freshness walks a dir the indexer skipped, every file in it reads as a phantom
69
+ // ADD on every query → re-index storm (bee-poc/Tekyz-CRM: 9,008 phantom adds from
70
+ // .venv + build bundles). [RULE] freshness-excludes-match-indexer-skipdirs
64
71
  const EXCLUDE_DIRS = new Set([
65
72
  'node_modules', '.git', 'dist', 'build', 'coverage',
66
73
  '.gsd-t', '.claude', '__pycache__', '.next', 'out',
74
+ '.cache', '.nyc_output', '.turbo',
75
+ '.venv', 'venv', 'env', '.dart_tool', 'Pods', 'vendor', '.gradle',
76
+ '.idea', '.vscode', 'tmp', '.tmp', 'site-packages',
67
77
  ]);
68
78
 
69
79
  function walkTree(dir, results = []) {
@@ -99,7 +109,7 @@ function walkTree(dir, results = []) {
99
109
  // a single open connection. If no db is passed, we open one from projectRoot.
100
110
 
101
111
  function openDb(projectRoot, explicitDbPath) {
102
- const Database = require('better-sqlite3');
112
+ const Database = require('./gsd-t-require-store.cjs').requireBetterSqlite();
103
113
  // Canonical store path is `.gsd-t/graph.db` (matches build_index's default,
104
114
  // the query CLI, and .gitignore). Accept an explicit path when the caller
105
115
  // already knows it (runFreshnessCheck passes the exact storePath).
@@ -70,6 +70,11 @@ const SKIP_DIRS = new Set([
70
70
  'node_modules', '.next', 'dist', 'build', '.git',
71
71
  '.cache', '__pycache__', 'coverage', '.nyc_output',
72
72
  'out', '.turbo',
73
+ // M96 follow-up: vendored / generated trees that aren't project source and
74
+ // explode the file count (bee-poc had 18K files from .venv + iOS build assets,
75
+ // hanging scip-typescript at 8.5GB). Exclude language vendor/build dirs.
76
+ '.venv', 'venv', 'env', '.dart_tool', 'Pods', 'vendor', '.gradle',
77
+ '.idea', '.vscode', 'tmp', '.tmp', 'site-packages',
73
78
  ]);
74
79
 
75
80
  // ── Content hash ──────────────────────────────────────────────────────────────
@@ -114,7 +119,7 @@ function enumerateFiles(root) {
114
119
  * Returns a better-sqlite3 Database instance.
115
120
  */
116
121
  function openStore(dbPath) {
117
- const Database = require('better-sqlite3');
122
+ const Database = require('./gsd-t-require-store.cjs').requireBetterSqlite();
118
123
  const db = new Database(dbPath);
119
124
  db.pragma('journal_mode = WAL');
120
125
  db.pragma('synchronous = NORMAL');
@@ -232,10 +232,10 @@ function loadSkippedFiles(storePath) {
232
232
  }
233
233
  if (!dbPath) return new Set();
234
234
 
235
- // Try to load better-sqlite3 it is a devDependency, may be absent
235
+ // Load the graph store engine via the M96 multi-location resolver.
236
236
  let Database;
237
237
  try {
238
- Database = require("better-sqlite3");
238
+ Database = require("./gsd-t-require-store.cjs").requireBetterSqlite();
239
239
  } catch (_e) {
240
240
  return new Set();
241
241
  }
@@ -394,7 +394,7 @@ function buildIndex(records, skippedFiles) {
394
394
  */
395
395
  function loadSqliteStore(dbPath) {
396
396
  let Database;
397
- try { Database = require("better-sqlite3"); }
397
+ try { Database = require("./gsd-t-require-store.cjs").requireBetterSqlite(); }
398
398
  catch (_e) { return null; }
399
399
  let db;
400
400
  try {
@@ -0,0 +1,89 @@
1
+ /**
2
+ * gsd-t-require-store.cjs
3
+ *
4
+ * M96 — Robust resolver for the graph runtime's NATIVE dependencies
5
+ * (better-sqlite3, tree-sitter, tree-sitter-typescript, tree-sitter-python).
6
+ *
7
+ * The graph runtime tools (gsd-t-graph-index / -freshness / -query-cli /
8
+ * -edge-extract) are COPIED into each project's `bin/` by update-all. A copied
9
+ * tool's bare `require('better-sqlite3')` / `require('tree-sitter')` resolves
10
+ * from the PROJECT's node_modules — which usually lacks these native modules.
11
+ * This helper resolves a module from, in order:
12
+ * 1. the normal resolution from this file's location
13
+ * 2. the project's own node_modules (cwd-based)
14
+ * 3. the GSD-T global package's node_modules (where `dependencies` install)
15
+ * 4. the GSD-T dev tree (this repo), a last-resort dev fallback
16
+ *
17
+ * All four are GSD-T `dependencies` (M96), so candidate 3 is the normal hit for a
18
+ * propagated copy. FAIL LOUD with a remediation message if all miss — never a
19
+ * cryptic MODULE_NOT_FOUND, and never a SILENT degrade to an empty graph.
20
+ *
21
+ * [RULE] graph-native-dep-resolved-or-fail-loud
22
+ */
23
+
24
+ 'use strict';
25
+
26
+ const path = require('node:path');
27
+ const { execSync } = require('node:child_process');
28
+
29
+ const _cache = new Map();
30
+ let _gsdtGlobalRoot; // memoized `npm root -g`/@tekyzinc/gsd-t
31
+
32
+ function gsdtGlobalNodeModules() {
33
+ if (_gsdtGlobalRoot !== undefined) return _gsdtGlobalRoot;
34
+ try {
35
+ const groot = execSync('npm root -g', { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
36
+ _gsdtGlobalRoot = path.join(groot, '@tekyzinc', 'gsd-t', 'node_modules');
37
+ } catch {
38
+ _gsdtGlobalRoot = null;
39
+ }
40
+ return _gsdtGlobalRoot;
41
+ }
42
+
43
+ /**
44
+ * Resolve and require a native graph dependency from any known location.
45
+ * @param {string} moduleName e.g. 'better-sqlite3', 'tree-sitter'
46
+ * @returns {*} the required module
47
+ * @throws {Error} a clear, actionable error if it cannot be found anywhere
48
+ */
49
+ function requireGraphDep(moduleName) {
50
+ if (_cache.has(moduleName)) return _cache.get(moduleName);
51
+
52
+ const tried = [];
53
+
54
+ // 1. Normal resolution (this file's own module graph).
55
+ try {
56
+ const m = require(moduleName);
57
+ _cache.set(moduleName, m);
58
+ return m;
59
+ } catch { tried.push('local module graph'); }
60
+
61
+ // 2–4. Explicit candidate directories.
62
+ const candidates = [
63
+ path.join(process.cwd(), 'node_modules', moduleName), // 2. project
64
+ gsdtGlobalNodeModules() ? path.join(gsdtGlobalNodeModules(), moduleName) : null, // 3. global pkg
65
+ path.join(__dirname, '..', 'node_modules', moduleName), // 4. GSD-T dev tree
66
+ ].filter(Boolean);
67
+
68
+ for (const c of candidates) {
69
+ tried.push(c);
70
+ try {
71
+ const m = require(c);
72
+ _cache.set(moduleName, m);
73
+ return m;
74
+ } catch { /* try next */ }
75
+ }
76
+
77
+ throw new Error(
78
+ `graph dependency '${moduleName}' unavailable — the code graph cannot run. ` +
79
+ 'Reinstall GSD-T (npx @tekyzinc/gsd-t install) so the native graph deps are present. ' +
80
+ `Searched: ${tried.join(' | ')}`
81
+ );
82
+ }
83
+
84
+ /** Back-compat convenience for the store engine. */
85
+ function requireBetterSqlite() {
86
+ return requireGraphDep('better-sqlite3');
87
+ }
88
+
89
+ module.exports = { requireGraphDep, requireBetterSqlite };
package/bin/gsd-t.js CHANGED
@@ -2682,6 +2682,10 @@ const PROJECT_BIN_TOOLS = [
2682
2682
  // [[project_code_graph_universal_consumer]] [[feedback_graph_is_architectural_anchor]]
2683
2683
  "gsd-t-graph-query-cli.cjs", "gsd-t-graph-index.cjs", "gsd-t-graph-freshness.cjs",
2684
2684
  "gsd-t-graph-edge-extract.cjs", "gsd-t-graph-scip-upgrade.cjs", "gsd-t-scip-reader.cjs",
2685
+ // M96 — multi-location resolver for the store engine (better-sqlite3), so a
2686
+ // copied tool finds the engine from the GSD-T global package, not the project's
2687
+ // own (usually absent) node_modules. Fail-loud with remediation if all miss.
2688
+ "gsd-t-require-store.cjs",
2685
2689
  ];
2686
2690
 
2687
2691
  // Files that older versions of this installer copied into project bin/ but
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tekyzinc/gsd-t",
3
- "version": "4.10.10",
3
+ "version": "4.10.12",
4
4
  "description": "GSD-T: Contract-Driven Development for Claude Code — 54 slash commands with headless-by-default workflow spawning, unattended supervisor relay with event stream, graph-powered code analysis, real-time agent dashboard, task telemetry, doc-ripple enforcement, backlog management, impact analysis, test sync, milestone archival, and PRD generation",
5
5
  "author": "Tekyz, Inc.",
6
6
  "license": "MIT",
@@ -31,12 +31,8 @@
31
31
  },
32
32
  "devDependencies": {
33
33
  "@playwright/test": "^1.55.0",
34
- "better-sqlite3": "^12.11.1",
35
34
  "graphology": "^0.26.0",
36
- "kuzu": "^0.11.3",
37
- "tree-sitter": "^0.21.1",
38
- "tree-sitter-python": "^0.21.0",
39
- "tree-sitter-typescript": "^0.23.2"
35
+ "kuzu": "^0.11.3"
40
36
  },
41
37
  "files": [
42
38
  "bin/",
@@ -49,5 +45,11 @@
49
45
  ],
50
46
  "engines": {
51
47
  "node": ">=16.0.0"
48
+ },
49
+ "dependencies": {
50
+ "better-sqlite3": "^12.11.1",
51
+ "tree-sitter": "^0.21.1",
52
+ "tree-sitter-typescript": "^0.23.2",
53
+ "tree-sitter-python": "^0.21.0"
52
54
  }
53
55
  }