@tekyzinc/gsd-t 4.9.13 → 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.
- package/CHANGELOG.md +24 -0
- package/README.md +1 -1
- package/bin/gsd-t-competition-judge.cjs +7 -1
- package/bin/gsd-t-context-brief-kinds/impact.cjs +91 -4
- package/bin/gsd-t-context-brief-kinds/partition.cjs +95 -0
- package/bin/gsd-t-context-brief-kinds/plan.cjs +110 -4
- package/bin/gsd-t-file-disjointness.cjs +319 -7
- package/bin/gsd-t-graph-anti-grep-lint.cjs +515 -0
- package/bin/gsd-t-graph-edge-extract.cjs +612 -0
- package/bin/gsd-t-graph-freshness.cjs +506 -0
- package/bin/gsd-t-graph-index.cjs +540 -0
- package/bin/gsd-t-graph-k1-sqlite-stream.cjs +251 -0
- package/bin/gsd-t-graph-query-cli.cjs +1182 -0
- package/bin/gsd-t-graph-scip-upgrade.cjs +440 -0
- package/bin/gsd-t-graph-store-bakeoff.cjs +889 -0
- package/bin/gsd-t-graph-synthetic-gen.cjs +304 -0
- package/bin/gsd-t-graph-ts-throughput.cjs +587 -0
- package/bin/gsd-t-scip-reader.cjs +167 -0
- package/bin/gsd-t.js +166 -48
- package/commands/gsd-t-debug.md +10 -0
- package/commands/gsd-t-design-build.md +10 -0
- package/commands/gsd-t-execute.md +15 -0
- package/commands/gsd-t-feature.md +15 -7
- package/commands/gsd-t-gap-analysis.md +17 -7
- package/commands/gsd-t-impact.md +25 -0
- package/commands/gsd-t-integrate.md +25 -0
- package/commands/gsd-t-partition.md +18 -0
- package/commands/gsd-t-plan.md +16 -0
- package/commands/gsd-t-populate.md +16 -5
- package/commands/gsd-t-prd.md +18 -0
- package/commands/gsd-t-project.md +19 -0
- package/commands/gsd-t-promote-debt.md +16 -5
- package/commands/gsd-t-qa.md +20 -8
- package/commands/gsd-t-quick.md +10 -0
- package/commands/gsd-t-scan.md +21 -3
- package/commands/gsd-t-test-sync.md +10 -0
- package/commands/gsd-t-verify.md +25 -0
- package/commands/gsd-t-wave.md +8 -0
- package/package.json +10 -2
- package/templates/workflows/gsd-t-debug.workflow.js +81 -0
- package/templates/workflows/gsd-t-integrate.workflow.js +47 -1
- package/templates/workflows/gsd-t-phase.workflow.js +99 -1
- package/templates/workflows/gsd-t-quick.workflow.js +64 -0
- package/templates/workflows/gsd-t-scan.workflow.js +200 -9
- package/templates/workflows/gsd-t-verify.workflow.js +50 -1
- package/bin/graph-cgc.js +0 -510
- package/bin/graph-indexer.js +0 -147
- package/bin/graph-overlay.js +0 -195
- package/bin/graph-parsers.js +0 -327
- package/bin/graph-query.js +0 -453
- package/bin/graph-store.js +0 -154
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gsd-t-scip-reader.cjs
|
|
3
|
+
*
|
|
4
|
+
* M95 — Read a SCIP index (`index.scip`, a protobuf emitted by scip-typescript /
|
|
5
|
+
* scip-python) and turn it into the data the graph needs to RESOLVE call edges:
|
|
6
|
+
*
|
|
7
|
+
* 1. symbolToDef : Map<scipSymbol, funcId> — where each symbol is DEFINED
|
|
8
|
+
* 2. fileRefs : Map<relPath, Array<{symbol, funcId, line}>> — every
|
|
9
|
+
* reference occurrence in that file, already resolved to the def funcId
|
|
10
|
+
*
|
|
11
|
+
* A funcId is the graph's canonical `relPath#funcName` (line-suffix dropped here;
|
|
12
|
+
* the indexer keys call edges on `relPath#name`-style ids — see resolveCallEdges).
|
|
13
|
+
*
|
|
14
|
+
* Why this exists: before M95 the SCIP "upgrade" only RAN scip-typescript and
|
|
15
|
+
* relabelled the tree-sitter edges 'compiler-accurate' without reading the output.
|
|
16
|
+
* This module reads the output so call targets actually resolve across files.
|
|
17
|
+
*
|
|
18
|
+
* The SCIP protobuf decoder is the one bundled inside scip-typescript
|
|
19
|
+
* (`dist/src/scip.js`). scip-typescript is a GSD-T install requirement (M95), so
|
|
20
|
+
* depending on its bundled decoder is legitimate. We locate it across install
|
|
21
|
+
* layouts and FAIL LOUD if absent (never silently degrade).
|
|
22
|
+
*
|
|
23
|
+
* [RULE] scip-resolution-reads-real-output — edges are derived from the .scip
|
|
24
|
+
* protobuf, never relabelled-in-place.
|
|
25
|
+
* [RULE] scip-symbol-to-funcid-deterministic — same .scip → same funcId map.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
'use strict';
|
|
29
|
+
|
|
30
|
+
const fs = require('node:fs');
|
|
31
|
+
const path = require('node:path');
|
|
32
|
+
const { execSync } = require('node:child_process');
|
|
33
|
+
|
|
34
|
+
// ── Locate scip-typescript's bundled SCIP protobuf decoder ───────────────────
|
|
35
|
+
let _scipProto = null;
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Load the bundled `scip.js` protobuf namespace from scip-typescript.
|
|
39
|
+
* Searches: local node_modules → global npm root → NODE_PATH.
|
|
40
|
+
* @returns {object|null} the `scip` namespace (Index, Document, …) or null.
|
|
41
|
+
*/
|
|
42
|
+
function loadScipProto() {
|
|
43
|
+
if (_scipProto) return _scipProto;
|
|
44
|
+
|
|
45
|
+
const candidates = [];
|
|
46
|
+
// 1. local node_modules (if scip-typescript is a project dep)
|
|
47
|
+
try {
|
|
48
|
+
candidates.push(require.resolve('@sourcegraph/scip-typescript/dist/src/scip.js'));
|
|
49
|
+
} catch { /* not local */ }
|
|
50
|
+
// 2. global npm root
|
|
51
|
+
try {
|
|
52
|
+
const groot = execSync('npm root -g', { encoding: 'utf8' }).trim();
|
|
53
|
+
candidates.push(path.join(groot, '@sourcegraph/scip-typescript/dist/src/scip.js'));
|
|
54
|
+
} catch { /* npm not reachable */ }
|
|
55
|
+
|
|
56
|
+
for (const c of candidates) {
|
|
57
|
+
try {
|
|
58
|
+
if (fs.existsSync(c)) {
|
|
59
|
+
const mod = require(c);
|
|
60
|
+
_scipProto = mod.scip || mod;
|
|
61
|
+
if (_scipProto && _scipProto.Index) return _scipProto;
|
|
62
|
+
}
|
|
63
|
+
} catch { /* try next */ }
|
|
64
|
+
}
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ── SCIP symbol → function name ──────────────────────────────────────────────
|
|
69
|
+
// A SCIP symbol looks like:
|
|
70
|
+
// "scip-typescript npm <pkg> <ver> src/`calc.ts`/computeTotal()."
|
|
71
|
+
// The trailing descriptor after the last '/' identifies the entity:
|
|
72
|
+
// "computeTotal()." → a method/function descriptor (ends with "().")
|
|
73
|
+
// "computeTotal()(x)" → a parameter (we ignore params for call resolution)
|
|
74
|
+
// We extract the bare name from a function/method descriptor.
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Extract the function name from a SCIP symbol, or null if the symbol is not a
|
|
78
|
+
* callable (parameter, type, module, etc.).
|
|
79
|
+
* @param {string} symbol
|
|
80
|
+
* @returns {string|null}
|
|
81
|
+
*/
|
|
82
|
+
function funcNameFromSymbol(symbol) {
|
|
83
|
+
if (!symbol || typeof symbol !== 'string') return null;
|
|
84
|
+
// Parameters look like "name().(param)" — skip (they contain "(...)" after "().")
|
|
85
|
+
// Methods/functions end with "()." possibly preceded by the name.
|
|
86
|
+
// Take the final descriptor (after the last unescaped '/').
|
|
87
|
+
const lastSlash = symbol.lastIndexOf('/');
|
|
88
|
+
const descriptor = lastSlash === -1 ? symbol : symbol.slice(lastSlash + 1);
|
|
89
|
+
// Method/function descriptor: "<name>()." — capture <name>
|
|
90
|
+
const m = descriptor.match(/^([A-Za-z_$][\w$]*)\(\)\.$/);
|
|
91
|
+
return m ? m[1] : null;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ── Read a SCIP index into resolution maps ───────────────────────────────────
|
|
95
|
+
|
|
96
|
+
const SYMBOL_ROLE_DEFINITION = 0x1; // SymbolRole.Definition bit
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Decode a `.scip` file and build resolution maps.
|
|
100
|
+
*
|
|
101
|
+
* @param {string} scipPath absolute path to an index.scip
|
|
102
|
+
* @returns {{ ok: true, symbolToDef: Map<string,string>,
|
|
103
|
+
* fileRefs: Map<string, Array<{symbol:string, funcId:string, line:number}>> }
|
|
104
|
+
* | { ok: false, reason: string }}
|
|
105
|
+
*/
|
|
106
|
+
function readScipIndex(scipPath) {
|
|
107
|
+
const proto = loadScipProto();
|
|
108
|
+
if (!proto) {
|
|
109
|
+
return { ok: false, reason: 'scip-decoder-unavailable' };
|
|
110
|
+
}
|
|
111
|
+
if (!fs.existsSync(scipPath)) {
|
|
112
|
+
return { ok: false, reason: 'scip-file-missing' };
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
let obj;
|
|
116
|
+
try {
|
|
117
|
+
const buf = fs.readFileSync(scipPath);
|
|
118
|
+
obj = proto.Index.deserialize(buf).toObject();
|
|
119
|
+
} catch (e) {
|
|
120
|
+
return { ok: false, reason: `scip-decode-failed: ${e.message}` };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const symbolToDef = new Map(); // scipSymbol → funcId (relPath#name)
|
|
124
|
+
const fileRefs = new Map(); // relPath → [{symbol, line}]
|
|
125
|
+
|
|
126
|
+
const docs = obj.documents || [];
|
|
127
|
+
|
|
128
|
+
// First pass: collect every DEFINITION occurrence → symbol → funcId.
|
|
129
|
+
for (const doc of docs) {
|
|
130
|
+
const relPath = doc.relative_path;
|
|
131
|
+
if (!relPath) continue;
|
|
132
|
+
for (const occ of doc.occurrences || []) {
|
|
133
|
+
const isDef = (occ.symbol_roles & SYMBOL_ROLE_DEFINITION) !== 0;
|
|
134
|
+
if (!isDef) continue;
|
|
135
|
+
const name = funcNameFromSymbol(occ.symbol);
|
|
136
|
+
if (!name) continue;
|
|
137
|
+
// funcId = the graph's file#name key
|
|
138
|
+
symbolToDef.set(occ.symbol, `${relPath}#${name}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Second pass: collect every REFERENCE occurrence per file, resolved to the def.
|
|
143
|
+
for (const doc of docs) {
|
|
144
|
+
const relPath = doc.relative_path;
|
|
145
|
+
if (!relPath) continue;
|
|
146
|
+
const refs = [];
|
|
147
|
+
for (const occ of doc.occurrences || []) {
|
|
148
|
+
const isDef = (occ.symbol_roles & SYMBOL_ROLE_DEFINITION) !== 0;
|
|
149
|
+
if (isDef) continue; // refs only
|
|
150
|
+
const name = funcNameFromSymbol(occ.symbol);
|
|
151
|
+
if (!name) continue; // only callable references
|
|
152
|
+
const funcId = symbolToDef.get(occ.symbol); // resolve to the def
|
|
153
|
+
if (!funcId) continue; // external/unresolvable → skip (stays floor)
|
|
154
|
+
const line = Array.isArray(occ.range) ? occ.range[0] : null;
|
|
155
|
+
refs.push({ symbol: occ.symbol, funcId, line });
|
|
156
|
+
}
|
|
157
|
+
if (refs.length) fileRefs.set(relPath, refs);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return { ok: true, symbolToDef, fileRefs };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
module.exports = {
|
|
164
|
+
loadScipProto,
|
|
165
|
+
funcNameFromSymbol,
|
|
166
|
+
readScipIndex,
|
|
167
|
+
};
|
package/bin/gsd-t.js
CHANGED
|
@@ -1501,6 +1501,41 @@ function installCgc() {
|
|
|
1501
1501
|
}
|
|
1502
1502
|
}
|
|
1503
1503
|
|
|
1504
|
+
// M95: install the SCIP indexers that give the code graph compiler-accurate
|
|
1505
|
+
// call-edge resolution. These are a GSD-T requirement (not "optional only to get
|
|
1506
|
+
// better") — the precise call graph (who-calls / test-impl / blast-radius across
|
|
1507
|
+
// imports) depends on them. Node-only npm packages (Apache-2.0 / MIT). Fail-loud
|
|
1508
|
+
// but non-blocking: if the global install needs sudo, warn with the manual
|
|
1509
|
+
// command rather than aborting the whole GSD-T install.
|
|
1510
|
+
function installScipIndexers() {
|
|
1511
|
+
const indexers = [
|
|
1512
|
+
{ bin: "scip-typescript", pkg: "@sourcegraph/scip-typescript", langs: "TS/JS" },
|
|
1513
|
+
{ bin: "scip-python", pkg: "@sourcegraph/scip-python", langs: "Python" },
|
|
1514
|
+
];
|
|
1515
|
+
for (const { bin, pkg, langs } of indexers) {
|
|
1516
|
+
let present = false;
|
|
1517
|
+
try {
|
|
1518
|
+
execFileSync("which", [bin], { encoding: "utf8", timeout: 3000, stdio: ["pipe", "pipe", "pipe"] });
|
|
1519
|
+
present = true;
|
|
1520
|
+
} catch { /* not present */ }
|
|
1521
|
+
|
|
1522
|
+
if (present) {
|
|
1523
|
+
info(`${bin} present (${langs} compiler-accurate edges)`);
|
|
1524
|
+
continue;
|
|
1525
|
+
}
|
|
1526
|
+
info(`Installing ${bin} (${langs} compiler-accurate code graph)...`);
|
|
1527
|
+
try {
|
|
1528
|
+
execFileSync("npm", ["install", "-g", pkg], {
|
|
1529
|
+
encoding: "utf8", timeout: 180000, stdio: ["pipe", "pipe", "pipe"],
|
|
1530
|
+
});
|
|
1531
|
+
success(`${bin} installed`);
|
|
1532
|
+
} catch (e) {
|
|
1533
|
+
warn(`${bin} install failed — code graph will use tree-sitter floor (approximate) for ${langs}`);
|
|
1534
|
+
info(`To enable compiler-accurate ${langs} edges: npm install -g ${pkg}`);
|
|
1535
|
+
}
|
|
1536
|
+
}
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1504
1539
|
// ─── Commands ────────────────────────────────────────────────────────────────
|
|
1505
1540
|
|
|
1506
1541
|
// Shared templates that slash-command prompts reference by predictable path.
|
|
@@ -1689,6 +1724,9 @@ async function doInstall(opts = {}) {
|
|
|
1689
1724
|
heading("Graph Engine (CGC)");
|
|
1690
1725
|
installCgc();
|
|
1691
1726
|
|
|
1727
|
+
heading("SCIP Indexers (Compiler-Accurate Code Graph — M95)");
|
|
1728
|
+
installScipIndexers();
|
|
1729
|
+
|
|
1692
1730
|
saveInstalledVersion();
|
|
1693
1731
|
|
|
1694
1732
|
showInstallSummary(gsdtCommands.length, utilityCommands.length);
|
|
@@ -2636,6 +2674,14 @@ const PROJECT_BIN_TOOLS = [
|
|
|
2636
2674
|
// M93 — jargon-gloss lint for written docs (the file surface the brevity-guard
|
|
2637
2675
|
// Stop hook can't reach). Propagated so a project's doc checks can invoke it.
|
|
2638
2676
|
"gsd-t-jargon-lint.cjs",
|
|
2677
|
+
// M94/M95 — code-graph runtime. The wired consumers (execute/wave disjointness,
|
|
2678
|
+
// debug, quick, impact, plan, scan) look for `bin/gsd-t-graph-query-cli.cjs` IN
|
|
2679
|
+
// THE PROJECT and fall back to grep when it is absent. Propagating the query CLI
|
|
2680
|
+
// plus its require-chain (indexer, freshness, edge-extract, SCIP upgrade + reader)
|
|
2681
|
+
// is what actually makes a project graph-aware instead of grep-dependent.
|
|
2682
|
+
// [[project_code_graph_universal_consumer]] [[feedback_graph_is_architectural_anchor]]
|
|
2683
|
+
"gsd-t-graph-query-cli.cjs", "gsd-t-graph-index.cjs", "gsd-t-graph-freshness.cjs",
|
|
2684
|
+
"gsd-t-graph-edge-extract.cjs", "gsd-t-graph-scip-upgrade.cjs", "gsd-t-scip-reader.cjs",
|
|
2639
2685
|
];
|
|
2640
2686
|
|
|
2641
2687
|
// Files that older versions of this installer copied into project bin/ but
|
|
@@ -3114,6 +3160,31 @@ function checkDoctorCgc() {
|
|
|
3114
3160
|
return issues;
|
|
3115
3161
|
}
|
|
3116
3162
|
|
|
3163
|
+
// M95: report SCIP indexer presence. SCIP gives the code graph compiler-accurate
|
|
3164
|
+
// call-edge resolution (who-calls / test-impl / blast-radius across imports).
|
|
3165
|
+
// Absence is a WARN (graph still works at tree-sitter floor), not a hard error.
|
|
3166
|
+
function checkDoctorScip() {
|
|
3167
|
+
let issues = 0;
|
|
3168
|
+
heading("SCIP Indexers (Compiler-Accurate Code Graph)");
|
|
3169
|
+
const indexers = [
|
|
3170
|
+
{ bin: "scip-typescript", langs: "TS/JS", pkg: "@sourcegraph/scip-typescript" },
|
|
3171
|
+
{ bin: "scip-python", langs: "Python", pkg: "@sourcegraph/scip-python" },
|
|
3172
|
+
];
|
|
3173
|
+
for (const { bin, langs, pkg } of indexers) {
|
|
3174
|
+
try {
|
|
3175
|
+
const ver = execFileSync(bin, ["--version"], {
|
|
3176
|
+
encoding: "utf8", timeout: 3000, stdio: ["pipe", "pipe", "pipe"],
|
|
3177
|
+
}).trim();
|
|
3178
|
+
success(`${bin} ${ver} (${langs} compiler-accurate)`);
|
|
3179
|
+
} catch {
|
|
3180
|
+
warn(`${bin} not installed — ${langs} edges fall back to tree-sitter floor (approximate)`);
|
|
3181
|
+
info(`Run: npm install -g ${pkg} (or reinstall GSD-T)`);
|
|
3182
|
+
issues++;
|
|
3183
|
+
}
|
|
3184
|
+
}
|
|
3185
|
+
return issues;
|
|
3186
|
+
}
|
|
3187
|
+
|
|
3117
3188
|
// Verify context meter wiring: hook registration, hook script presence,
|
|
3118
3189
|
// config validity, and a local estimation dry-run.
|
|
3119
3190
|
// Returns number of issues (RED results). Mirrors checkDoctorCgc shape.
|
|
@@ -3355,6 +3426,7 @@ async function doDoctor(opts) {
|
|
|
3355
3426
|
issues += checkDoctorInstallation();
|
|
3356
3427
|
issues += await checkDoctorProject(opts);
|
|
3357
3428
|
issues += checkDoctorCgc();
|
|
3429
|
+
issues += checkDoctorScip();
|
|
3358
3430
|
// M61 D1: Context Meter retired (token-budget.cjs + context-meter hook deleted).
|
|
3359
3431
|
// checkDoctorContextMeter would emit phantom errors for the deleted subsystem
|
|
3360
3432
|
// and tell the user to reinstall it. Native /context replaces the meter.
|
|
@@ -3506,68 +3578,111 @@ function doChangelog() {
|
|
|
3506
3578
|
}
|
|
3507
3579
|
|
|
3508
3580
|
// ─── Graph ──────────────────────────────────────────────────────────────────
|
|
3581
|
+
// M94 Fix-1: the M20–M21 dead engine (graph-indexer / graph-store / graph-query)
|
|
3582
|
+
// is REMOVED. All entity-graph verbs (status / who-imports / who-calls / blast-radius
|
|
3583
|
+
// / index / query) now delegate to the M94 D5 query CLI
|
|
3584
|
+
// (bin/gsd-t-graph-query-cli.cjs). [RULE] graph-status-live
|
|
3585
|
+
// [RULE] graph-rewire-no-dangling-export
|
|
3586
|
+
//
|
|
3587
|
+
// Preserved intact: `gsd-t graph --output json|table` and `gsd-t graph tasks`
|
|
3588
|
+
// which print the M44 task-DAG (bin/gsd-t-task-graph.cjs) — a SEPARATE working
|
|
3589
|
+
// feature unrelated to the codebase entity graph. (Destructive Action Guard.)
|
|
3590
|
+
|
|
3591
|
+
/**
|
|
3592
|
+
* Delegate a verb to the D5 graph query CLI.
|
|
3593
|
+
* Returns the JSON envelope (already parsed) or { ok: false, reason: '...' }.
|
|
3594
|
+
* Prints human-readable output when interactive=true (the default).
|
|
3595
|
+
*
|
|
3596
|
+
* [RULE] graph-status-live — NEVER hits the dead M20–M21 "No graph index found" path.
|
|
3597
|
+
*/
|
|
3598
|
+
function _graphQueryCli(verbAndArgs) {
|
|
3599
|
+
const { spawnSync } = require("child_process");
|
|
3600
|
+
const cliPath = require("path").join(__dirname, "gsd-t-graph-query-cli.cjs");
|
|
3601
|
+
const result = spawnSync(process.execPath, [cliPath].concat(verbAndArgs), {
|
|
3602
|
+
encoding: "utf8",
|
|
3603
|
+
cwd: process.cwd(),
|
|
3604
|
+
timeout: 30000,
|
|
3605
|
+
});
|
|
3606
|
+
if (result.error) {
|
|
3607
|
+
return { ok: false, reason: "graph-unavailable", detail: result.error.message };
|
|
3608
|
+
}
|
|
3609
|
+
const stdout = (result.stdout || "").trim();
|
|
3610
|
+
if (!stdout) {
|
|
3611
|
+
return { ok: false, reason: "graph-unavailable", detail: result.stderr || "no output" };
|
|
3612
|
+
}
|
|
3613
|
+
try {
|
|
3614
|
+
return JSON.parse(stdout);
|
|
3615
|
+
} catch {
|
|
3616
|
+
return { ok: false, reason: "graph-unavailable", detail: stdout };
|
|
3617
|
+
}
|
|
3618
|
+
}
|
|
3509
3619
|
|
|
3620
|
+
/**
|
|
3621
|
+
* gsd-t graph index
|
|
3622
|
+
* Builds the codebase entity graph via D3 (bin/gsd-t-graph-index.cjs).
|
|
3623
|
+
* [RULE] graph-rewire-no-dangling-export
|
|
3624
|
+
*/
|
|
3510
3625
|
function doGraphIndex() {
|
|
3511
3626
|
heading("GSD-T Graph — Index");
|
|
3512
|
-
const
|
|
3513
|
-
const
|
|
3514
|
-
const result =
|
|
3515
|
-
|
|
3516
|
-
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
}
|
|
3523
|
-
} else {
|
|
3524
|
-
error("Indexing failed");
|
|
3627
|
+
const { spawnSync } = require("child_process");
|
|
3628
|
+
const idxPath = require("path").join(__dirname, "gsd-t-graph-index.cjs");
|
|
3629
|
+
const result = spawnSync(process.execPath, [idxPath, "build", "--repo", process.cwd()], {
|
|
3630
|
+
encoding: "utf8",
|
|
3631
|
+
cwd: process.cwd(),
|
|
3632
|
+
stdio: ["ignore", "inherit", "inherit"],
|
|
3633
|
+
timeout: 300000,
|
|
3634
|
+
});
|
|
3635
|
+
if (result.status !== 0 && result.status !== null) {
|
|
3636
|
+
error("Graph index build failed — see output above");
|
|
3525
3637
|
}
|
|
3526
3638
|
}
|
|
3527
3639
|
|
|
3640
|
+
/**
|
|
3641
|
+
* gsd-t graph status
|
|
3642
|
+
* Queries live index state via D5 CLI.
|
|
3643
|
+
* [RULE] graph-status-live — NEVER returns "No graph index found" from the dead path.
|
|
3644
|
+
* [RULE] graph-rewire-no-dangling-export
|
|
3645
|
+
*/
|
|
3528
3646
|
function doGraphStatus() {
|
|
3529
3647
|
heading("GSD-T Graph — Status");
|
|
3530
|
-
const
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3648
|
+
const envelope = _graphQueryCli(["status"]);
|
|
3649
|
+
if (!envelope.ok) {
|
|
3650
|
+
warn(`Graph index unavailable: ${envelope.reason || "graph-unavailable"}`);
|
|
3651
|
+
info("Run: gsd-t graph index");
|
|
3652
|
+
return;
|
|
3653
|
+
}
|
|
3654
|
+
// envelope from D5 CLI: { ok, verb:"status", indexed, storeSize, fileCount, tier, ... }
|
|
3655
|
+
if (envelope.indexed === false) {
|
|
3656
|
+
warn("No graph index found.");
|
|
3657
|
+
info("Run: gsd-t graph index");
|
|
3535
3658
|
return;
|
|
3536
3659
|
}
|
|
3537
|
-
success(`
|
|
3538
|
-
info(`
|
|
3539
|
-
info(`
|
|
3540
|
-
|
|
3541
|
-
info(`Duration: ${meta.duration}ms`);
|
|
3542
|
-
const fileCount = Object.keys(meta.fileHashes || {}).length;
|
|
3543
|
-
info(`Files tracked: ${fileCount}`);
|
|
3660
|
+
success(`Graph index: ${envelope.fileCount || 0} files`);
|
|
3661
|
+
if (envelope.tier) info(`Tier: ${envelope.tier}`);
|
|
3662
|
+
if (envelope.storeSize !== undefined) info(`Store size: ${envelope.storeSize} bytes`);
|
|
3663
|
+
if (envelope.detail) log(JSON.stringify(envelope, null, 2));
|
|
3544
3664
|
}
|
|
3545
3665
|
|
|
3666
|
+
/**
|
|
3667
|
+
* gsd-t graph query <verb> [args...]
|
|
3668
|
+
* Delegates the verb directly to the D5 CLI.
|
|
3669
|
+
* [RULE] graph-rewire-no-dangling-export
|
|
3670
|
+
*/
|
|
3546
3671
|
function doGraphQuery(args) {
|
|
3547
|
-
const
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3551
|
-
error("Usage: gsd-t graph query <type> [params...]");
|
|
3552
|
-
info("Types: getEntity, getEntities, getCallers, getCallees,");
|
|
3553
|
-
info(" findDeadCode, findDuplicates, findCircularDeps,");
|
|
3554
|
-
info(" getDomainBoundaryViolations, getIndexStatus");
|
|
3672
|
+
const verb = args[0];
|
|
3673
|
+
if (!verb) {
|
|
3674
|
+
error("Usage: gsd-t graph query <verb> [target]");
|
|
3675
|
+
info("Verbs: status, who-imports, who-calls, blast-radius, cluster, dead-code, orphan, dangling, test-impl");
|
|
3555
3676
|
return;
|
|
3556
3677
|
}
|
|
3557
|
-
const
|
|
3558
|
-
|
|
3559
|
-
const [k, v] = args[i].split("=");
|
|
3560
|
-
if (k && v) params[k] = v;
|
|
3561
|
-
}
|
|
3562
|
-
const result = gq.query(type, params, root);
|
|
3563
|
-
log(JSON.stringify(result, null, 2));
|
|
3678
|
+
const envelope = _graphQueryCli([verb].concat(args.slice(1)));
|
|
3679
|
+
log(JSON.stringify(envelope, null, 2));
|
|
3564
3680
|
}
|
|
3565
3681
|
|
|
3566
3682
|
function doGraph(args) {
|
|
3567
3683
|
// M44 D1-T4: `gsd-t graph --output json|table` prints the task-graph DAG
|
|
3568
|
-
// (parsed from .gsd-t/domains/*/tasks.md)
|
|
3569
|
-
//
|
|
3570
|
-
// indexer) and remain unchanged.
|
|
3684
|
+
// (parsed from .gsd-t/domains/*/tasks.md). This is a SEPARATE working feature
|
|
3685
|
+
// (Destructive Action Guard — preserved intact). [RULE] graph-rewire-no-dangling-export
|
|
3571
3686
|
const outIdx = args.indexOf("--output");
|
|
3572
3687
|
if (outIdx !== -1) {
|
|
3573
3688
|
const fmt = args[outIdx + 1] || "json";
|
|
@@ -3575,13 +3690,16 @@ function doGraph(args) {
|
|
|
3575
3690
|
}
|
|
3576
3691
|
const sub = args[0] || "status";
|
|
3577
3692
|
switch (sub) {
|
|
3578
|
-
case "index":
|
|
3579
|
-
case "status":
|
|
3580
|
-
case "query":
|
|
3581
|
-
case "
|
|
3693
|
+
case "index": doGraphIndex(); break;
|
|
3694
|
+
case "status": doGraphStatus(); break;
|
|
3695
|
+
case "query": doGraphQuery(args.slice(1)); break;
|
|
3696
|
+
case "who-imports": { const e = _graphQueryCli(["who-imports", args[1] || ""]); log(JSON.stringify(e, null, 2)); break; }
|
|
3697
|
+
case "who-calls": { const e = _graphQueryCli(["who-calls", args[1] || ""]); log(JSON.stringify(e, null, 2)); break; }
|
|
3698
|
+
case "blast-radius": { const e = _graphQueryCli(["blast-radius", args[1] || ""]); log(JSON.stringify(e, null, 2)); break; }
|
|
3699
|
+
case "tasks": doGraphTaskOutput(args[1] || "table"); break;
|
|
3582
3700
|
default:
|
|
3583
3701
|
error(`Unknown graph subcommand: ${sub}`);
|
|
3584
|
-
info("Usage: gsd-t graph [index|status|query|tasks]");
|
|
3702
|
+
info("Usage: gsd-t graph [index|status|query|who-imports|who-calls|blast-radius|tasks]");
|
|
3585
3703
|
info(" gsd-t graph --output json|table (task DAG)");
|
|
3586
3704
|
}
|
|
3587
3705
|
}
|
package/commands/gsd-t-debug.md
CHANGED
|
@@ -90,6 +90,16 @@ The Workflow runs up to 2 cycles. Cycle 2 receives Cycle 1's failed hypothesis t
|
|
|
90
90
|
- `complete` → re-run verify (`/gsd-t-verify`); auto-advance.
|
|
91
91
|
- `needs-human` → present root-cause hypothesis + nextStepsIfNotResolved to the user. Do NOT spawn a third cycle; that's the Prime Rule.
|
|
92
92
|
|
|
93
|
+
## Graph-Enhanced Debugging — READER + WRITER Pattern (M94-D11)
|
|
94
|
+
|
|
95
|
+
Debug applies the **WRITER pattern** from `graph-consumer-wiring-contract.md`:
|
|
96
|
+
|
|
97
|
+
**READER half (localization):** Before the fix agent reasons over code, the debug workflow queries `blast-radius` and `who-calls` to localize the bug's call chain — which callers reach the failing function, what is downstream of the changed symbol. This replaces grep-reconstructed call-chain discovery. The structural slice is injected into the debug agent's context.
|
|
98
|
+
|
|
99
|
+
**WRITER half (re-index after fix):** After the fix lands, the workflow triggers a re-index of the edited files so downstream graph queries see fresh edges (`graph-freshness-contract.md` D4 surface — `freshness_check_on_query` over the touched set). `[RULE] debug-reader-and-writer-both`.
|
|
100
|
+
|
|
101
|
+
**FAIL-LOUD on graph-unavailable:** On `{ok:false, reason:"graph-unavailable"}`, the debug workflow surfaces `"graph unavailable — fix it (gsd-t graph status)"` and halts the graph-query step. It does NOT fall back to grep for the structural question. The existing debug-loop logic (2-cycle cap, loop-ledger halt) is NOT disrupted — the graph query is additive, injected before the fix agent receives context.
|
|
102
|
+
|
|
93
103
|
## Contract-Boundary Debugging
|
|
94
104
|
|
|
95
105
|
If the bug is at a domain contract boundary (one domain calls another via API/schema/component contract), the debug cycle agent is instructed to:
|
|
@@ -2,6 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
This command delegates to the **JavaScript orchestrator** for ironclad flow control. Do NOT attempt to run the build pipeline inline — the orchestrator handles Claude spawning, measurement, review gates, and feedback processing deterministically.
|
|
4
4
|
|
|
5
|
+
## Graph-Enhanced Design Build — WRITER Pattern (M94-D11)
|
|
6
|
+
|
|
7
|
+
Design-build applies the **WRITER pattern** from `graph-consumer-wiring-contract.md`:
|
|
8
|
+
|
|
9
|
+
**READER half (who-imports / cluster):** Before generating code, the build pipeline queries `gsd-t graph who-imports` on the target design-contract files to discover which existing components import the target (impact of the generated code on existing consumers), and `gsd-t graph cluster` to surface tightly-coupled file groups relevant to the component's context. This replaces grep/raw-read for the dependency structure question. The structural slice is injected into the code-generation agent's context. `[RULE] design-build-writer-pattern`.
|
|
10
|
+
|
|
11
|
+
**WRITER half:** After each tier (elements → widgets → pages) generates files, the pipeline triggers a re-index of the generated files (`freshness_check_on_query` from `graph-freshness-contract.md` D4 surface) so the next tier's `who-imports` / `cluster` query sees the new edges from the generated components. `[RULE] design-build-writer-pattern`.
|
|
12
|
+
|
|
13
|
+
**FAIL-LOUD on graph-unavailable:** On `{ok:false, reason:"graph-unavailable"}`, the structural query surfaces `"graph unavailable — fix it (gsd-t graph status)"` — the pipeline does NOT fall back to grep for the structural question. `[RULE] consumer-structural-grep-removed`.
|
|
14
|
+
|
|
5
15
|
## Step 1: Launch the Orchestrator
|
|
6
16
|
|
|
7
17
|
```bash
|
|
@@ -57,6 +57,21 @@ The Workflow returns:
|
|
|
57
57
|
- `status === "verify-failed"`: domains completed but verify-gate found regressions. Invoke `gsd-t-debug` (or its Workflow) before retry.
|
|
58
58
|
- `status === "failed"`: a domain reported `failed`, integrate failed, or preflight blocked. Read `domainResults` for the blocking entry.
|
|
59
59
|
|
|
60
|
+
## Graph-Aware Disjointness (SAFETY-CRITICAL — M94-D11)
|
|
61
|
+
|
|
62
|
+
The file-disjointness check (`bin/gsd-t-file-disjointness.cjs`) is **graph-aware** as of M94. It uses `gsd-t graph blast-radius` and `gsd-t graph who-imports` to detect **transitive dependency overlap** — two domains whose touched files share a transitive dependency (one's files import a module that the other's files also import) are NOT disjoint even if their declared `Touches` lists have no literal overlap.
|
|
63
|
+
|
|
64
|
+
**FAIL-LOUD halt on graph-unavailable:**
|
|
65
|
+
On `graph-unavailable`, the disjointness check **FAILS LOUD and HALTS** — it does NOT fan out on a grep-reconstructed disjointness guess. A grep-reconstructed disjointness guess risks a concurrent-edit conflict (two agents editing overlapping code). This is the safety-critical invariant: `[RULE] execute-disjointness-fail-loud-halts-never-grep-guess`.
|
|
66
|
+
|
|
67
|
+
**Bootstrap escape hatch (fresh repo or parser regression):**
|
|
68
|
+
`graph-unavailable` in a fresh repo (no index yet built) or after a parser regression must not permanently brick parallel execution. When the graph is genuinely unavailable:
|
|
69
|
+
- The HALT message clearly states `graph-unavailable` (not `graph-says-non-disjoint`) and surfaces the remediation: `gsd-t graph status` and `gsd-t graph build`.
|
|
70
|
+
- The operator may pass `--disjointness-fallback=touches-only` to fall back to the literal-Touches-overlap check with a **loud announced WARNING** (never silent). This escape is NOT applicable to `graph-says-non-disjoint` verdicts — a real non-disjoint block stays absolute.
|
|
71
|
+
- `[RULE] disjointness-bootstrap-escape-not-a-no-recourse-brick`
|
|
72
|
+
|
|
73
|
+
**WRITER half:** After each domain's edits land, the workflow triggers a freshness pass over the touched set so downstream graph queries see fresh edges (`graph-freshness-contract.md` D4 surface). `[RULE] execute-disjointness-graph-aware-dependency-overlap`.
|
|
74
|
+
|
|
60
75
|
## Document Ripple
|
|
61
76
|
|
|
62
77
|
After execute completes, ensure these are updated in the same commit chain:
|
|
@@ -52,18 +52,26 @@ Build a mental model of: "How does this codebase work today?"
|
|
|
52
52
|
|
|
53
53
|
Note what you find — this informs Step 3's Multi-Consumer Check and Step 4's milestone ordering.
|
|
54
54
|
|
|
55
|
-
## Step 1.5: Graph-
|
|
55
|
+
## Step 1.5: Graph Structural Slice — blast-radius + who-imports (M94-D10)
|
|
56
56
|
|
|
57
57
|
```bash
|
|
58
|
-
node scripts/gsd-t-watch-state.js advance --agent-id "$GSD_T_AGENT_ID" --parent-id "${GSD_T_PARENT_AGENT_ID:-null}" --command gsd-t-feature --step 1 --step-label ".5: Graph
|
|
58
|
+
node scripts/gsd-t-watch-state.js advance --agent-id "$GSD_T_AGENT_ID" --parent-id "${GSD_T_PARENT_AGENT_ID:-null}" --command gsd-t-feature --step 1 --step-label ".5: Graph Structural Slice" 2>/dev/null || true
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
3. Feed these findings into the Impact Analysis in Step 3
|
|
61
|
+
**[RULE] plan-feature-gapanalysis-use-graph-not-grep** — the feature agent MUST use the graph
|
|
62
|
+
CLI for blast-radius and who-imports questions (new-feature impact on existing code), NOT
|
|
63
|
+
grep/raw-read to reconstruct dependents.
|
|
65
64
|
|
|
66
|
-
|
|
65
|
+
The phase Workflow (`gsd-t-phase.workflow.js`) automatically queries `gsd-t graph blast-radius`
|
|
66
|
+
for the feature phase and injects the pre-computed blast-radius slice into the agent context.
|
|
67
|
+
Use this slice to identify which existing code is in the blast radius of the new feature's
|
|
68
|
+
touch points — catching impact via call edges that a grep over import patterns would MISS.
|
|
69
|
+
|
|
70
|
+
**On `graph-unavailable`:** the phase Workflow surfaces a LOUD message and the feature agent
|
|
71
|
+
FAILS LOUD — it does NOT silently skip this step or fall back to grep for the structural
|
|
72
|
+
blast-radius / who-imports question. Run `gsd-t graph status` to diagnose.
|
|
73
|
+
|
|
74
|
+
Graph consumer manifest row: `commands/gsd-t-feature.md | templates/workflows/gsd-t-phase.workflow.js | reader | blast-radius,who-imports | grep-reconstructed blast-radius/dependent discovery`
|
|
67
75
|
|
|
68
76
|
## Step 2: Understand the Feature
|
|
69
77
|
|
|
@@ -32,18 +32,28 @@ Read (if they exist):
|
|
|
32
32
|
3. `docs/requirements.md` — existing requirements
|
|
33
33
|
4. `docs/architecture.md` — system structure
|
|
34
34
|
|
|
35
|
-
## Step 1.5: Graph-
|
|
35
|
+
## Step 1.5: Graph Structural Slice — who-imports + dead-code (M94-D10)
|
|
36
36
|
|
|
37
37
|
```bash
|
|
38
|
-
node scripts/gsd-t-watch-state.js advance --agent-id "$GSD_T_AGENT_ID" --parent-id "${GSD_T_PARENT_AGENT_ID:-null}" --command gsd-t-gap-analysis --step 1 --step-label ".5: Graph
|
|
38
|
+
node scripts/gsd-t-watch-state.js advance --agent-id "$GSD_T_AGENT_ID" --parent-id "${GSD_T_PARENT_AGENT_ID:-null}" --command gsd-t-gap-analysis --step 1 --step-label ".5: Graph Structural Slice" 2>/dev/null || true
|
|
39
39
|
```
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
3. Feed these findings into the classification step to improve evidence quality
|
|
41
|
+
**[RULE] plan-feature-gapanalysis-use-graph-not-grep** — the gap-analysis agent MUST use the
|
|
42
|
+
graph CLI for dead-code and who-imports questions (requirements-vs-code coverage gaps), NOT
|
|
43
|
+
grep/raw-read to reconstruct the dependency or dead-code picture.
|
|
45
44
|
|
|
46
|
-
|
|
45
|
+
The phase Workflow (`gsd-t-phase.workflow.js`) automatically queries `gsd-t graph dead-code`
|
|
46
|
+
for the gap-analysis phase and injects the pre-computed dead-code slice into the agent context.
|
|
47
|
+
Use this slice to:
|
|
48
|
+
1. Identify implemented-but-disconnected code (dead-code entries that may indicate a gap — a
|
|
49
|
+
feature was implemented but never wired into a consumer).
|
|
50
|
+
2. Identify which requirements map to code entities via real import/call edges (`who-imports`).
|
|
51
|
+
|
|
52
|
+
**On `graph-unavailable`:** the phase Workflow surfaces a LOUD message and the gap-analysis
|
|
53
|
+
agent FAILS LOUD — it does NOT silently skip this step or fall back to grep for the structural
|
|
54
|
+
dead-code / who-imports question. Run `gsd-t graph status` to diagnose.
|
|
55
|
+
|
|
56
|
+
Graph consumer manifest row: `commands/gsd-t-gap-analysis.md | templates/workflows/gsd-t-phase.workflow.js | reader | who-imports,dead-code | grep-reconstructed dead-code/dependency discovery`
|
|
47
57
|
|
|
48
58
|
## Step 2: Parse Requirements
|
|
49
59
|
|
package/commands/gsd-t-impact.md
CHANGED
|
@@ -14,6 +14,31 @@ The agent analyzes the downstream effects of proposed changes: what might break,
|
|
|
14
14
|
|
|
15
15
|
Read `.gsd-t/progress.md`, the relevant domain `tasks.md`, and `docs/architecture.md`/`.gsd-t/contracts/` for the surfaces in scope.
|
|
16
16
|
|
|
17
|
+
## Step 1.5: Graph Structural Slice — blast-radius (M94-D10)
|
|
18
|
+
|
|
19
|
+
**[RULE] impact-uses-blast-radius-not-grep** — the impact agent MUST use the graph CLI
|
|
20
|
+
to answer the downstream-effect (blast-radius) question, NOT grep/raw-read to reconstruct
|
|
21
|
+
dependents.
|
|
22
|
+
|
|
23
|
+
The phase Workflow (`gsd-t-phase.workflow.js`) automatically queries `gsd-t graph blast-radius`
|
|
24
|
+
for the impact phase and injects the pre-computed blast-radius slice into the agent context
|
|
25
|
+
before reasoning begins. The agent receives the structural answer and MUST use it — no grep
|
|
26
|
+
reconstruction of import/call dependents is permitted.
|
|
27
|
+
|
|
28
|
+
**On `graph-unavailable`:** the phase Workflow surfaces a LOUD message:
|
|
29
|
+
```
|
|
30
|
+
⚠ graph unavailable — structural blast-radius slice NOT injected (fix it: gsd-t graph status).
|
|
31
|
+
NO grep fallback — structural question unanswered.
|
|
32
|
+
```
|
|
33
|
+
The impact agent FAILS LOUD on graph-unavailable — it does NOT silently fall back to grep for
|
|
34
|
+
the structural blast-radius question. Run `gsd-t graph status` to diagnose.
|
|
35
|
+
|
|
36
|
+
The `blast-radius` verb returns the downstream impact set as the UNION of the import-graph
|
|
37
|
+
and call-graph reverse-reachable sets from the target (transitive). This catches dependents
|
|
38
|
+
reachable only via call edges that a grep over import patterns would MISS.
|
|
39
|
+
|
|
40
|
+
Graph consumer manifest row: `commands/gsd-t-impact.md | templates/workflows/gsd-t-phase.workflow.js | reader | blast-radius | grep-reconstructed dependent set`
|
|
41
|
+
|
|
17
42
|
## Step 2: Resolve the active model profile (M86 — invoke-time injection)
|
|
18
43
|
|
|
19
44
|
Before calling the Workflow, resolve the active model profile to build the `overrides` map:
|
|
@@ -25,6 +25,31 @@ It does NOT re-do work that domain workers already did, and does NOT run the ful
|
|
|
25
25
|
|
|
26
26
|
Read `.gsd-t/progress.md` and verify all required domain workers have completed (status `complete` in their tasks.md). Read `.gsd-t/contracts/m{NN}-integration-points.md` for the shared-file matrix.
|
|
27
27
|
|
|
28
|
+
## Step 1.5: Graph Structural Slice — who-imports + blast-radius (M94-D10, ADDITIVE)
|
|
29
|
+
|
|
30
|
+
**[RULE] integrate-uses-graph-for-wiring-verification** — the integrate Workflow MUST use
|
|
31
|
+
the graph CLI `who-imports` and `blast-radius` verbs to verify cross-domain wiring (real
|
|
32
|
+
import/call edges across the seam), NOT LLM-reconstructed wiring by reading files.
|
|
33
|
+
|
|
34
|
+
**[RULE] verify-integrate-graph-additive-announced-not-hard-fail** — integrate's graph query
|
|
35
|
+
degrades ANNOUNCED on graph-unavailable, NOT as a hard-fail of the entire integrate run.
|
|
36
|
+
|
|
37
|
+
The integrate Workflow (`gsd-t-integrate.workflow.js`) queries `gsd-t graph who-imports` and
|
|
38
|
+
`gsd-t graph blast-radius` to verify that domains are actually wired (real edges exist across
|
|
39
|
+
the cross-domain seam). The integrate agent receives this slice and uses it to confirm
|
|
40
|
+
wiring — rather than reading files and inferring the connection.
|
|
41
|
+
|
|
42
|
+
**On `graph-unavailable`:** the integrate Workflow records a WARNING and CONTINUES its
|
|
43
|
+
cross-domain wire-up work:
|
|
44
|
+
```
|
|
45
|
+
⚠ graph unavailable — structural wiring-check skipped, fix it (gsd-t graph status)
|
|
46
|
+
```
|
|
47
|
+
It does NOT hard-fail integrate solely due to graph unavailability — integrate must always be
|
|
48
|
+
able to run. This is the bootstrap carve-out (PRE-MORTEM Finding 3). Integrate does NOT
|
|
49
|
+
silently grep for the structural wiring question.
|
|
50
|
+
|
|
51
|
+
Graph consumer manifest row (lint-exempt per verify+integrate carve-out): `commands/gsd-t-integrate.md | templates/workflows/gsd-t-integrate.workflow.js | reader | who-imports,blast-radius | LLM-read-reconstructed cross-domain wiring verification`
|
|
52
|
+
|
|
28
53
|
## Step 2: Invoke the integrate Workflow
|
|
29
54
|
|
|
30
55
|
Call the `Workflow` tool with:
|