@tekyzinc/gsd-t 5.11.27 → 5.11.29
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 +78 -0
- package/README.md +1 -1
- package/bin/gsd-t-graph-query-cli.cjs +60 -2
- package/bin/gsd-t.js +180 -0
- package/package.json +2 -2
- package/templates/CLAUDE-global.md +19 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,84 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to GSD-T are documented here. Updated with each release.
|
|
4
4
|
|
|
5
|
+
## [5.11.29] - 2026-08-11
|
|
6
|
+
|
|
7
|
+
### Fixed — 20 of 27 projects had no usable code graph, and nothing said so
|
|
8
|
+
|
|
9
|
+
A binvoice session reached for the graph, found none, and read an 827-file
|
|
10
|
+
project by grep. `update-all` had reported that project as current for months,
|
|
11
|
+
because it was: every file GSD-T ships was in place. The graph is not a shipped
|
|
12
|
+
file — it is built state, created only when someone runs `gsd-t graph index` by
|
|
13
|
+
hand, and no propagation step creates it.
|
|
14
|
+
|
|
15
|
+
Checking for it exposed a second, larger failure. M99 moved the store from
|
|
16
|
+
`.gsd-t/graph.db` to `.gsd-t/graphDB/graph.db` — it changed where the code LOOKS
|
|
17
|
+
without moving what was already there. 18 projects still held a real, populated
|
|
18
|
+
index at the old path that every tool walked straight past.
|
|
19
|
+
|
|
20
|
+
never built 2 (binvoice, newman)
|
|
21
|
+
at the old path 18 — a full index, invisible to the tooling
|
|
22
|
+
working 7
|
|
23
|
+
|
|
24
|
+
**Absence is now repaired, not reported.** Per David's rule: a missing graph is
|
|
25
|
+
BUILT, a stale one is UPDATED, a misplaced one is MOVED, and grep is reserved
|
|
26
|
+
for content that cannot be indexed at all (`.md`, `.sql`, `.json`, config,
|
|
27
|
+
prose). The prior contract said HALT on an absent graph — correct 20 times here,
|
|
28
|
+
and it would have repaired nothing.
|
|
29
|
+
|
|
30
|
+
The rule now also governs plain conversational sessions. The binvoice failure
|
|
31
|
+
was not a wired command; it was ordinary work, and the contract only ever
|
|
32
|
+
covered `/gsd-t-*` commands.
|
|
33
|
+
|
|
34
|
+
- `bin/gsd-t.js`: `graphState()` tells the two faults apart; `migrateLegacyGraph()`
|
|
35
|
+
moves a pre-M99 store (the resolver's own migration, already written for M99
|
|
36
|
+
and never called on existing projects); `buildGraph()` indexes from scratch and
|
|
37
|
+
verifies a store actually landed rather than trusting the indexer's exit code.
|
|
38
|
+
A failed repair is reported per project and never counted as fixed.
|
|
39
|
+
- `.gsd-t/contracts/graph-consumer-wiring-contract.md`: FAIL-LOUD now repairs
|
|
40
|
+
before halting.
|
|
41
|
+
- `~/.claude/CLAUDE.md` + `templates/CLAUDE-global.md`: the rule reaches every
|
|
42
|
+
session, not only wired commands.
|
|
43
|
+
- `test/m112-graph-health-check.test.js`: 11 regressions.
|
|
44
|
+
|
|
45
|
+
Run once here: 18 stores moved, 2 graphs built (binvoice 639 files/56,822 edges,
|
|
46
|
+
newman 610 files/27,582 edges), all verified answering.
|
|
47
|
+
|
|
48
|
+
## [5.11.28] - 2026-08-11
|
|
49
|
+
|
|
50
|
+
### Fixed — the graph stored the alias edges, then could not find them (two bugs)
|
|
51
|
+
|
|
52
|
+
`who-imports src/lib/db.ts` returned 5 importers on hilo-figma-atos.
|
|
53
|
+
`grep -rl '@/lib/db' src` returned 793. Found by David, who halted the scan
|
|
54
|
+
rather than running it on a graph that was answering wrongly.
|
|
55
|
+
|
|
56
|
+
v5.11.26 taught the INDEXER to expand `@/lib/db` into `src/lib/db`, and it does
|
|
57
|
+
— the edges are in the database, correctly. It never met the QUERY side, which
|
|
58
|
+
is what turns `src/lib/db` into the real file id `src/lib/db.ts`. Two
|
|
59
|
+
independent bugs there, and fixing either alone still returns nothing:
|
|
60
|
+
|
|
61
|
+
1. The resolver opened with `if (!dst.startsWith(".")) return dst` — an expanded
|
|
62
|
+
alias has no leading dot, so it was classed as an external package like
|
|
63
|
+
"react" and returned untouched. The extension was never appended.
|
|
64
|
+
2. The set of known files was built from FUNCTION nodes, so a module exporting
|
|
65
|
+
only constants, types, or re-exports was not in it. Atos's `src/lib/db.ts`
|
|
66
|
+
exports a constant. The indexer already records every file it walked in the
|
|
67
|
+
`files` table — the authoritative list was stored all along.
|
|
68
|
+
|
|
69
|
+
- `bin/gsd-t-graph-query-cli.cjs`: resolve non-relative targets against the file
|
|
70
|
+
set (packages still pass through untouched, and an expanded alias is NOT
|
|
71
|
+
re-joined to the importer's directory); read the file set from the `files`
|
|
72
|
+
table, announcing the degradation on an older graph that lacks it.
|
|
73
|
+
- `test/m112-alias-query-resolution.test.js`: 7 regressions, all going through
|
|
74
|
+
the QUERY rather than the stored edge.
|
|
75
|
+
|
|
76
|
+
Every v5.11.26 test asserted the edge was STORED correctly. Not one asked
|
|
77
|
+
whether it could then be FOUND — the feature was tested at the write and called
|
|
78
|
+
done. These tests close that gap.
|
|
79
|
+
|
|
80
|
+
Requires `gsd-t graph index` only if the graph predates the `files` table;
|
|
81
|
+
otherwise the fix applies to existing indexes immediately.
|
|
82
|
+
|
|
5
83
|
## [5.11.27] - 2026-08-11
|
|
6
84
|
|
|
7
85
|
### Fixed — the scan's volume probe returned a stand-in, and the whole run was built on it
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# GSD-T: Contract-Driven Development for Claude Code
|
|
2
2
|
|
|
3
|
-
**v5.11.
|
|
3
|
+
**v5.11.29** - 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.
|
|
@@ -437,10 +437,68 @@ function loadSqliteStore(dbPath) {
|
|
|
437
437
|
// TS/JS routinely omit the extension; the graph keys on real file ids.
|
|
438
438
|
// Every indexed file appears as the `file` of its nodes (the indexer does not
|
|
439
439
|
// emit dedicated FILE nodes), so collect the distinct file set from there.
|
|
440
|
-
|
|
440
|
+
// [RULE] query-file-set-comes-from-files-table-not-inferred-from-functions
|
|
441
|
+
//
|
|
442
|
+
// hilo-figma-atos, 2026-08-11, the second half of the same miss. This set
|
|
443
|
+
// decides whether an extensionless import target can be matched to a real
|
|
444
|
+
// file. Built from node rows, it contains only files that declare a
|
|
445
|
+
// FUNCTION — so `src/lib/db.ts`, which exports a constant, was not in it,
|
|
446
|
+
// and no query could ever resolve an import of it. A file of constants,
|
|
447
|
+
// types, or re-exports is exactly the kind of shared module a whole codebase
|
|
448
|
+
// imports, and it was invisible.
|
|
449
|
+
//
|
|
450
|
+
// The indexer records every file it walked in the `files` table. That is the
|
|
451
|
+
// authoritative list; inferring one from functions was always an
|
|
452
|
+
// approximation of a fact already stored.
|
|
453
|
+
let fileIds;
|
|
454
|
+
const hasFilesTable = db
|
|
455
|
+
.prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='files'")
|
|
456
|
+
.get();
|
|
457
|
+
if (hasFilesTable) {
|
|
458
|
+
fileIds = new Set(
|
|
459
|
+
db.prepare("SELECT file FROM files").all().map((r) => norm(r.file)).filter(Boolean)
|
|
460
|
+
);
|
|
461
|
+
} else {
|
|
462
|
+
// A graph built before the files table existed. Node-derived is what this
|
|
463
|
+
// has always done — announced, because it under-reports imports of any
|
|
464
|
+
// file that declares no function.
|
|
465
|
+
fileIds = new Set(nodes.map((n) => norm(n.file)).filter(Boolean));
|
|
466
|
+
process.stderr.write(
|
|
467
|
+
"[graph] this index predates the files table — imports of constant-only/type-only files may be missed; re-run `gsd-t graph index`\n"
|
|
468
|
+
);
|
|
469
|
+
}
|
|
441
470
|
const EXTS = ["", ".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py", "/index.ts", "/index.tsx", "/index.js"];
|
|
471
|
+
// [RULE] query-resolves-expanded-alias-not-only-relative
|
|
472
|
+
//
|
|
473
|
+
// hilo-figma-atos, 2026-08-11: `who-imports src/lib/db.ts` returned 5 of 793
|
|
474
|
+
// real importers. The 788 missing ones all wrote `@/lib/db`.
|
|
475
|
+
//
|
|
476
|
+
// The two halves of the alias fix did not meet. The indexer (v5.11.26)
|
|
477
|
+
// expands `@/lib/db` to `src/lib/db` — correct, and stored. But this
|
|
478
|
+
// resolver, which is what appends the file extension, skipped it: the gate
|
|
479
|
+
// below returned early for anything not starting with ".", treating an
|
|
480
|
+
// expanded project path exactly like the package import `react`. So the edge
|
|
481
|
+
// sat in the database as `src/lib/db` while the query asked for
|
|
482
|
+
// `src/lib/db.ts`, and the two never met.
|
|
483
|
+
//
|
|
484
|
+
// A relative specifier is resolved against its source file's directory; an
|
|
485
|
+
// expanded alias is ALREADY project-relative and must not be — joining it to
|
|
486
|
+
// the importer's directory would produce `src/app/src/lib/db`. So the two
|
|
487
|
+
// take different routes to the same place: add the extension either way.
|
|
442
488
|
const resolveDst = (srcFile, dst) => {
|
|
443
|
-
if (typeof dst !== "string" || !dst
|
|
489
|
+
if (typeof dst !== "string" || !dst) return dst;
|
|
490
|
+
|
|
491
|
+
if (!dst.startsWith(".")) {
|
|
492
|
+
// Not relative. Either a package ("react", "next/navigation") or an
|
|
493
|
+
// alias the indexer already expanded to a project path. The file set is
|
|
494
|
+
// what tells them apart — a package matches nothing in it.
|
|
495
|
+
const base = norm(dst);
|
|
496
|
+
for (const ext of EXTS) {
|
|
497
|
+
if (fileIds.has(base + ext)) return base + ext;
|
|
498
|
+
}
|
|
499
|
+
return dst; // genuinely external — keep it exactly as written
|
|
500
|
+
}
|
|
501
|
+
|
|
444
502
|
const base = path.posix.normalize(path.posix.join(path.posix.dirname(norm(srcFile)), norm(dst)));
|
|
445
503
|
// Prefer an actual indexed file id (try the bare path, then common extensions).
|
|
446
504
|
for (const ext of EXTS) {
|
package/bin/gsd-t.js
CHANGED
|
@@ -2917,10 +2917,129 @@ function createProjectChangelog(projectDir, projectName) {
|
|
|
2917
2917
|
}
|
|
2918
2918
|
}
|
|
2919
2919
|
|
|
2920
|
+
/**
|
|
2921
|
+
* Does this project have a built code graph, and does it have code worth one?
|
|
2922
|
+
*
|
|
2923
|
+
* [RULE] health-reports-missing-graph-never-assumes-built
|
|
2924
|
+
*
|
|
2925
|
+
* binvoice, 2026-08-11: a session reached for the graph, found nothing, and fell
|
|
2926
|
+
* back to grep — in an 827-file project. The tooling was installed and
|
|
2927
|
+
* propagated like everywhere else; the index had simply never been built.
|
|
2928
|
+
* Nothing builds it automatically, so a project has a graph only if someone once
|
|
2929
|
+
* ran `gsd-t graph index` there by hand.
|
|
2930
|
+
*
|
|
2931
|
+
* Nothing ever said so. `update-all` reported the project as current, because it
|
|
2932
|
+
* was — every file it ships was in place. The gap lived in state no propagation
|
|
2933
|
+
* step creates, and stayed invisible until a session hit it mid-task.
|
|
2934
|
+
*
|
|
2935
|
+
* Reported, never auto-built: indexing a large repo is slow, and doing it to 33
|
|
2936
|
+
* of them inside an update would be a surprise nobody asked for.
|
|
2937
|
+
*/
|
|
2938
|
+
function graphState(projectDir) {
|
|
2939
|
+
// Where the store lives is the resolver's answer alone. If it cannot answer,
|
|
2940
|
+
// this check HALTS the caller rather than reporting a project as healthy — a
|
|
2941
|
+
// silent "graph fine" here is exactly the blindness being fixed.
|
|
2942
|
+
const resolver = require("./gsd-t-graph-store-resolver.cjs");
|
|
2943
|
+
const storePath = resolver.resolveStorePath(projectDir);
|
|
2944
|
+
if (storePath && fs.existsSync(storePath)) return { missing: false, files: 0 };
|
|
2945
|
+
|
|
2946
|
+
// A graph at the pre-M99 location (.gsd-t/graph.db) EXISTS — it is just where
|
|
2947
|
+
// the resolver no longer looks. Telling the user to build one would be wrong
|
|
2948
|
+
// twice over: the work is already done, and the real problem (a store the
|
|
2949
|
+
// tooling cannot find) would go unnamed.
|
|
2950
|
+
const legacyPath = resolver.resolveLegacyStorePath(projectDir);
|
|
2951
|
+
if (legacyPath && fs.existsSync(legacyPath)) return { missing: false, files: 0, legacy: true };
|
|
2952
|
+
|
|
2953
|
+
let n = 0;
|
|
2954
|
+
const SKIP = new Set(["node_modules", ".git", ".next", "dist", "build", ".venv", "venv", "out", "coverage"]);
|
|
2955
|
+
const EXT = new Set([".ts", ".tsx", ".js", ".jsx", ".mjs", ".cjs", ".py"]);
|
|
2956
|
+
const walk = (dir, depth) => {
|
|
2957
|
+
if (depth > 6) return;
|
|
2958
|
+
if (n > 400) return; // enough to answer "is this a real codebase?"
|
|
2959
|
+
let entries;
|
|
2960
|
+
try {
|
|
2961
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
2962
|
+
} catch (_e) {
|
|
2963
|
+
// An unreadable subdirectory makes the count a floor, not a lie: the
|
|
2964
|
+
// result is only ever compared against a low threshold, and a miscount
|
|
2965
|
+
// downward can only omit a project from a report, never invent one.
|
|
2966
|
+
return;
|
|
2967
|
+
}
|
|
2968
|
+
for (const e of entries) {
|
|
2969
|
+
if (e.isDirectory()) {
|
|
2970
|
+
if (SKIP.has(e.name)) continue;
|
|
2971
|
+
if (e.name.startsWith(".")) continue;
|
|
2972
|
+
walk(path.join(dir, e.name), depth + 1);
|
|
2973
|
+
continue;
|
|
2974
|
+
}
|
|
2975
|
+
if (EXT.has(path.extname(e.name))) n++;
|
|
2976
|
+
}
|
|
2977
|
+
};
|
|
2978
|
+
walk(projectDir, 0);
|
|
2979
|
+
|
|
2980
|
+
// A project with almost no source has nothing for a graph to map; calling that
|
|
2981
|
+
// "missing" would be noise on every docs-only or config-only repo.
|
|
2982
|
+
return { missing: n >= 25, files: n };
|
|
2983
|
+
}
|
|
2984
|
+
|
|
2985
|
+
/**
|
|
2986
|
+
* Move a graph that sits at the pre-M99 path into the one the tooling reads.
|
|
2987
|
+
*
|
|
2988
|
+
* The store already holds a full index — M99 changed where every tool LOOKS
|
|
2989
|
+
* without moving what was there, so the graph was present and unreachable. The
|
|
2990
|
+
* resolver's own migration does the move, checkpoints the write-ahead log, and
|
|
2991
|
+
* verifies the result is readable.
|
|
2992
|
+
*/
|
|
2993
|
+
function migrateLegacyGraph(projectDir) {
|
|
2994
|
+
try {
|
|
2995
|
+
const r = require("./gsd-t-graph-store-resolver.cjs").migrateGraphStore(projectDir);
|
|
2996
|
+
if (r && r.migrated) return { ok: true };
|
|
2997
|
+
return { ok: false, err: (r && r.reason) || "migration reported no result" };
|
|
2998
|
+
} catch (e) {
|
|
2999
|
+
return { ok: false, err: e.message || String(e) };
|
|
3000
|
+
}
|
|
3001
|
+
}
|
|
3002
|
+
|
|
3003
|
+
/**
|
|
3004
|
+
* Build a project's code graph from scratch.
|
|
3005
|
+
*
|
|
3006
|
+
* Slow on a large repo, so the caller announces it before this runs. A failure
|
|
3007
|
+
* is returned, never swallowed: a project left without a graph must say so, so
|
|
3008
|
+
* the next session halts instead of quietly grepping.
|
|
3009
|
+
*/
|
|
3010
|
+
function buildGraph(projectDir) {
|
|
3011
|
+
try {
|
|
3012
|
+
const idx = path.join(__dirname, "gsd-t-graph-index.cjs");
|
|
3013
|
+
const r = require("child_process").spawnSync(process.execPath, [idx], {
|
|
3014
|
+
cwd: projectDir,
|
|
3015
|
+
encoding: "utf8",
|
|
3016
|
+
timeout: 15 * 60 * 1000,
|
|
3017
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
3018
|
+
});
|
|
3019
|
+
if (r.error) return { ok: false, err: r.error.message };
|
|
3020
|
+
if (r.status !== 0) {
|
|
3021
|
+
const tail = String(r.stderr || r.stdout || "").trim().split("\n").slice(-2).join(" ");
|
|
3022
|
+
return { ok: false, err: `exit ${r.status}${tail ? ` — ${tail}` : ""}` };
|
|
3023
|
+
}
|
|
3024
|
+
// The indexer reporting success is not proof a store landed. Verify the file
|
|
3025
|
+
// the tooling will actually read.
|
|
3026
|
+
const storePath = require("./gsd-t-graph-store-resolver.cjs").resolveStorePath(projectDir);
|
|
3027
|
+
if (!fs.existsSync(storePath)) return { ok: false, err: "indexer reported success but no store was written" };
|
|
3028
|
+
return { ok: true };
|
|
3029
|
+
} catch (e) {
|
|
3030
|
+
return { ok: false, err: e.message || String(e) };
|
|
3031
|
+
}
|
|
3032
|
+
}
|
|
3033
|
+
|
|
2920
3034
|
async function checkProjectHealth(projects) {
|
|
2921
3035
|
heading("Project Health");
|
|
2922
3036
|
const playwrightMissing = [];
|
|
2923
3037
|
const swaggerMissing = [];
|
|
3038
|
+
const graphMissing = [];
|
|
3039
|
+
const graphLegacy = [];
|
|
3040
|
+
const graphMigrated = [];
|
|
3041
|
+
const graphBuilt = [];
|
|
3042
|
+
const graphRepairFailed = [];
|
|
2924
3043
|
const playwrightAutoInstalled = [];
|
|
2925
3044
|
const playwrightInstallFailed = [];
|
|
2926
3045
|
|
|
@@ -2929,6 +3048,42 @@ async function checkProjectHealth(projects) {
|
|
|
2929
3048
|
const name = path.basename(projectDir);
|
|
2930
3049
|
if (!hasPlaywright(projectDir)) playwrightMissing.push(name);
|
|
2931
3050
|
if (hasApi(projectDir) && !hasSwagger(projectDir)) swaggerMissing.push(name);
|
|
3051
|
+
const g = graphState(projectDir);
|
|
3052
|
+
if (g.missing) graphMissing.push({ name, dir: projectDir, files: g.files });
|
|
3053
|
+
if (g.legacy) graphLegacy.push({ name, dir: projectDir });
|
|
3054
|
+
}
|
|
3055
|
+
|
|
3056
|
+
// [RULE] graph-missing-is-built-not-reported
|
|
3057
|
+
//
|
|
3058
|
+
// A missing graph is not a status to report — it is work to do. Every session
|
|
3059
|
+
// in a project without one silently falls back to grep, which reads a fraction
|
|
3060
|
+
// of what the graph knows and answers a different question. Reporting it and
|
|
3061
|
+
// moving on leaves that in place until a human notices, which for binvoice
|
|
3062
|
+
// (827 files) meant months.
|
|
3063
|
+
//
|
|
3064
|
+
// Two different faults, two different repairs:
|
|
3065
|
+
//
|
|
3066
|
+
// · at the old path — the store EXISTS and holds a full index. M99 changed
|
|
3067
|
+
// where the tooling looks without moving what was there, so 18 projects
|
|
3068
|
+
// were reading past a perfectly good graph. This is a file move plus a
|
|
3069
|
+
// write-ahead-log checkpoint, and takes no time at all.
|
|
3070
|
+
// · never built — there is nothing to move. Indexing is genuinely slow
|
|
3071
|
+
// on a large repo, so this is announced before it runs rather than
|
|
3072
|
+
// appearing as an unexplained pause.
|
|
3073
|
+
//
|
|
3074
|
+
// A repair that fails is REPORTED and the project is left as it was. It is not
|
|
3075
|
+
// retried differently and never half-applied: a graph that came up short would
|
|
3076
|
+
// answer confidently with partial data, which is worse than not having one.
|
|
3077
|
+
for (const { name, dir } of graphLegacy) {
|
|
3078
|
+
const r = migrateLegacyGraph(dir);
|
|
3079
|
+
if (r.ok) graphMigrated.push(name);
|
|
3080
|
+
else graphRepairFailed.push({ name, what: "move", err: r.err });
|
|
3081
|
+
}
|
|
3082
|
+
for (const { name, dir, files } of graphMissing) {
|
|
3083
|
+
log(` building code graph for ${name} (${files}+ source files) — first build, this takes a while`);
|
|
3084
|
+
const r = buildGraph(dir);
|
|
3085
|
+
if (r.ok) graphBuilt.push(name);
|
|
3086
|
+
else graphRepairFailed.push({ name, what: "build", err: r.err });
|
|
2932
3087
|
}
|
|
2933
3088
|
|
|
2934
3089
|
// M50 D1: auto-install Playwright for any UI project that's missing it.
|
|
@@ -2947,6 +3102,19 @@ async function checkProjectHealth(projects) {
|
|
|
2947
3102
|
}
|
|
2948
3103
|
}
|
|
2949
3104
|
|
|
3105
|
+
if (graphMigrated.length > 0) {
|
|
3106
|
+
success(`Code graph moved to where the tooling reads it: ${graphMigrated.join(", ")}`);
|
|
3107
|
+
}
|
|
3108
|
+
if (graphBuilt.length > 0) {
|
|
3109
|
+
success(`Code graph built: ${graphBuilt.join(", ")}`);
|
|
3110
|
+
}
|
|
3111
|
+
if (graphRepairFailed.length > 0) {
|
|
3112
|
+
for (const f of graphRepairFailed) {
|
|
3113
|
+
warn(` ${f.name} — graph ${f.what} FAILED: ${f.err}`);
|
|
3114
|
+
}
|
|
3115
|
+
info("Those projects have no usable graph. Fix before working in them — a session there cannot answer structural questions.");
|
|
3116
|
+
}
|
|
3117
|
+
|
|
2950
3118
|
if (playwrightMissing.length === 0 && swaggerMissing.length === 0) {
|
|
2951
3119
|
success("All projects have Playwright and Swagger configured");
|
|
2952
3120
|
} else {
|
|
@@ -2978,6 +3146,11 @@ async function checkProjectHealth(projects) {
|
|
|
2978
3146
|
return {
|
|
2979
3147
|
playwrightMissing,
|
|
2980
3148
|
swaggerMissing,
|
|
3149
|
+
graphMissing,
|
|
3150
|
+
graphLegacy,
|
|
3151
|
+
graphMigrated,
|
|
3152
|
+
graphBuilt,
|
|
3153
|
+
graphRepairFailed,
|
|
2981
3154
|
playwrightAutoInstalled,
|
|
2982
3155
|
playwrightInstallFailed,
|
|
2983
3156
|
};
|
|
@@ -3155,6 +3328,7 @@ async function doUpdateAll() {
|
|
|
3155
3328
|
playwrightMissing,
|
|
3156
3329
|
swaggerMissing,
|
|
3157
3330
|
playwrightAutoInstalled,
|
|
3331
|
+
graphMissing,
|
|
3158
3332
|
} = await checkProjectHealth(projects);
|
|
3159
3333
|
showUpdateAllSummary(
|
|
3160
3334
|
projects.length,
|
|
@@ -3163,6 +3337,7 @@ async function doUpdateAll() {
|
|
|
3163
3337
|
swaggerMissing,
|
|
3164
3338
|
syncCount,
|
|
3165
3339
|
playwrightAutoInstalled,
|
|
3340
|
+
graphMissing,
|
|
3166
3341
|
);
|
|
3167
3342
|
}
|
|
3168
3343
|
|
|
@@ -3693,6 +3868,7 @@ function showUpdateAllSummary(
|
|
|
3693
3868
|
swaggerMissing,
|
|
3694
3869
|
syncCount,
|
|
3695
3870
|
playwrightAutoInstalled,
|
|
3871
|
+
graphMissing,
|
|
3696
3872
|
) {
|
|
3697
3873
|
log("");
|
|
3698
3874
|
heading("Update All Complete");
|
|
@@ -3706,6 +3882,9 @@ function showUpdateAllSummary(
|
|
|
3706
3882
|
log(` Auto-installed Playwright in: ${playwrightAutoInstalled.length} project(s)`);
|
|
3707
3883
|
}
|
|
3708
3884
|
if (swaggerMissing.length > 0) log(` Missing Swagger: ${swaggerMissing.length}`);
|
|
3885
|
+
if (Array.isArray(graphMissing) && graphMissing.length > 0) {
|
|
3886
|
+
log(` No code graph: ${graphMissing.length}`);
|
|
3887
|
+
}
|
|
3709
3888
|
if (syncCount > 0) log(` Global rules synced: ${syncCount}`);
|
|
3710
3889
|
log("");
|
|
3711
3890
|
}
|
|
@@ -5336,6 +5515,7 @@ function showHelp() {
|
|
|
5336
5515
|
// ─── Exports (for testing) ───────────────────────────────────────────────────
|
|
5337
5516
|
|
|
5338
5517
|
module.exports = {
|
|
5518
|
+
graphState,
|
|
5339
5519
|
validateProjectName,
|
|
5340
5520
|
applyTokens,
|
|
5341
5521
|
normalizeEol,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tekyzinc/gsd-t",
|
|
3
|
-
"version": "5.11.
|
|
4
|
-
"description": "GSD-T: Contract-Driven Development for Claude Code
|
|
3
|
+
"version": "5.11.29",
|
|
4
|
+
"description": "GSD-T: Contract-Driven Development for Claude Code \u2014 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",
|
|
7
7
|
"repository": {
|
|
@@ -310,6 +310,25 @@ When any GSD-T command creates or modifies an API endpoint:
|
|
|
310
310
|
|
|
311
311
|
This applies during: `gsd-t-execute`, `gsd-t-quick`, `gsd-t-integrate`, `gsd-t-wave`, and any command that touches API code.
|
|
312
312
|
|
|
313
|
+
## Code Graph — build it, never grep around it (MANDATORY)
|
|
314
|
+
|
|
315
|
+
**Read code structure through the graph. If the graph is missing, BUILD it. If it is out of date, UPDATE it. Grep only where the answer cannot be indexed.**
|
|
316
|
+
|
|
317
|
+
```
|
|
318
|
+
NEED A STRUCTURAL ANSWER? (what imports this, who calls this, what breaks if I change it)
|
|
319
|
+
├── Graph present and fresh? → query it
|
|
320
|
+
├── Graph missing? → `gsd-t graph index` — BUILD IT, then query
|
|
321
|
+
├── Graph stale? → re-index the touched set, then query
|
|
322
|
+
├── Graph at the old path? → it EXISTS; move it (`gsd-t graph index`), then query
|
|
323
|
+
└── Build/repair FAILED? → HALT and say so. Never answer the structural question by grep.
|
|
324
|
+
```
|
|
325
|
+
|
|
326
|
+
**Grep is correct ONLY where the content cannot be indexed** — `.md`, `.sql`, `.json`, `.sh`, config, prose, comments. For anything about code structure, grep is not a weaker answer, it is a **different and wrong one**: it matches text, the question is about relationships.
|
|
327
|
+
|
|
328
|
+
**This governs plain conversational work, not just `/gsd-t-*` commands.** The failure that produced this rule (binvoice, 2026-08-11) was an ordinary session: it reached for the graph, found none, grepped an 827-file project, and nothing objected. Checking the graph's existence before reasoning about code is the first move, not a fallback.
|
|
329
|
+
|
|
330
|
+
**Absence is a repairable condition, not a stop sign.** When this was checked across the machine, 20 of 27 registered projects had no usable graph — 2 never built, 18 holding a real index at a path the tooling stopped reading after the store moved. Every one of those sessions had been grepping. `gsd-t update-all` now repairs both automatically and reports any it could not.
|
|
331
|
+
|
|
313
332
|
## Prime Rule
|
|
314
333
|
KEEP GOING. Only stop for:
|
|
315
334
|
1. Unrecoverable errors after 2 fix attempts (delegate to `gsd-t headless --debug-loop` first — only stop if exit code 4)
|