@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.
Files changed (51) hide show
  1. package/CHANGELOG.md +24 -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 +99 -1
  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
@@ -16,6 +16,24 @@ The agent decomposes the milestone into 2–5 file-disjoint domains, writes `.gs
16
16
 
17
17
  Read `.gsd-t/progress.md` to determine the active milestone and its defined scope. If a scan exists and is stale (>10 commits or >14 days), the agent refreshes the relevant dimensions before partitioning.
18
18
 
19
+ ## Step 1.5: Graph Structural Slice — cluster (M94-D10)
20
+
21
+ **[RULE] partition-project-use-cluster-verb** — the partition agent MUST use the graph CLI
22
+ `cluster` verb to identify tightly-coupled file groups when suggesting domain cuts, NOT
23
+ LLM-reconstructed coupling by reading files.
24
+
25
+ The phase Workflow (`gsd-t-phase.workflow.js`) automatically queries `gsd-t graph cluster`
26
+ for the partition phase and injects the pre-computed coupling clusters into the agent context.
27
+ The `cluster` verb returns tightly-coupled file groups via a DETERMINISTIC Jaccard metric over
28
+ import neighbourhoods — same input, same output, reproducible grouping. Domain cuts should be
29
+ made along low-coupling boundaries identified by this slice.
30
+
31
+ **On `graph-unavailable`:** the phase Workflow surfaces a LOUD message and the partition agent
32
+ FAILS LOUD — it does NOT silently fall back to LLM-reconstructed coupling estimates.
33
+ Run `gsd-t graph status` to diagnose.
34
+
35
+ Graph consumer manifest row: `commands/gsd-t-partition.md | templates/workflows/gsd-t-phase.workflow.js | reader | cluster | LLM-reconstructed file-coupling for domain-boundary decisions`
36
+
19
37
  ## Step 2: Resolve the active model profile (M86 — invoke-time injection)
20
38
 
21
39
  Before calling the Workflow, resolve the active model profile to build the `overrides` map:
@@ -14,6 +14,22 @@ For each domain, the agent writes atomic `tasks.md` entries in parallel-executio
14
14
 
15
15
  Read `.gsd-t/progress.md` and each domain's `scope.md`/`constraints.md`. The partition output is the input to planning.
16
16
 
17
+ ## Step 1.5: Graph Structural Slice — who-imports (M94-D10)
18
+
19
+ **[RULE] plan-feature-gapanalysis-use-graph-not-grep** — the plan agent MUST use the graph
20
+ CLI to answer touched-file dependency questions (who-imports, sequencing), NOT grep/raw-read.
21
+
22
+ The phase Workflow (`gsd-t-phase.workflow.js`) automatically queries `gsd-t graph who-imports`
23
+ for the plan phase and injects the pre-computed dependency slice into the agent context.
24
+ The agent uses this to sequence tasks by real dependency order (which files depend on which)
25
+ without reconstructing the dependency graph by reading imports manually.
26
+
27
+ **On `graph-unavailable`:** the phase Workflow surfaces a LOUD message and the plan agent
28
+ FAILS LOUD — it does NOT silently fall back to grep for the structural who-imports question.
29
+ Run `gsd-t graph status` to diagnose.
30
+
31
+ Graph consumer manifest row: `commands/gsd-t-plan.md | templates/workflows/gsd-t-phase.workflow.js | reader | who-imports,blast-radius | grep-reconstructed dependency ordering`
32
+
17
33
  ## Step 2: Resolve the active model profile (M86 — invoke-time injection)
18
34
 
19
35
  Before calling the Workflow, resolve the active model profile to build the `overrides` map:
@@ -30,13 +30,24 @@ Scan this codebase and populate the GSD-T documentation. Analyze the actual code
30
30
  - Document Deployment from CI/CD configs, Dockerfiles, cloud configs
31
31
  - Document Logging and Monitoring from any logging setup or dashboard configs
32
32
 
33
- ## Graph-Enhanced Population
33
+ ## Graph Structural Slice — who-imports + cluster (M94-D10)
34
34
 
35
- If `.gsd-t/graph/meta.json` exists (graph index is available):
36
- 1. Use `getEntities` and `getImports` to auto-populate architecture docs more accurately entity relationships map directly to component diagrams and data flow sections
37
- 2. Use `getEntitiesByDomain` to pre-fill domain scope files with precise file ownership
35
+ **[RULE] populate-promotedebt-prd-use-graph-not-grep** the populate agent MUST use the graph
36
+ CLI to answer structural questions about file dependencies and coupling, NOT grep/raw-read to
37
+ reconstruct the import graph.
38
38
 
39
- If graph is not available, rely on filesystem scanning as usual.
39
+ The phase Workflow (`gsd-t-phase.workflow.js`) automatically queries `gsd-t graph who-imports`
40
+ for the populate phase and injects the pre-computed dependency slice into the agent context.
41
+ Use this slice to:
42
+ 1. Auto-populate architecture docs with precise entity relationships (real import edges, not
43
+ reconstructed from grep patterns).
44
+ 2. Pre-fill domain scope files with accurate file ownership derived from coupling clusters.
45
+
46
+ **On `graph-unavailable`:** the phase Workflow surfaces a LOUD message and the populate agent
47
+ FAILS LOUD — it does NOT silently fall back to filesystem scanning for the structural
48
+ who-imports / cluster question. Run `gsd-t graph status` to diagnose.
49
+
50
+ Graph consumer manifest row: `commands/gsd-t-populate.md | templates/workflows/gsd-t-phase.workflow.js | reader | who-imports,cluster | grep/filesystem-scan for import/coupling structure`
40
51
 
41
52
  ## For .gsd-t/progress.md:
42
53
  - Set Milestone 1: "Documentation Baseline"
@@ -14,6 +14,24 @@ The agent takes a user's idea — however rough — reads available GSD-T projec
14
14
 
15
15
  Read any existing `docs/requirements.md`, `docs/architecture.md`, and `.gsd-t/progress.md`. Capture the user's idea from `$ARGUMENTS`.
16
16
 
17
+ ## Step 1.5: Graph Structural Slice — cluster (M94-D10)
18
+
19
+ **[RULE] populate-promotedebt-prd-use-graph-not-grep** — the PRD agent MUST use the graph
20
+ CLI `cluster` verb for structure-aware decomposition when an existing codebase is present,
21
+ NOT LLM-estimated coupling.
22
+
23
+ The phase Workflow (`gsd-t-phase.workflow.js`) automatically queries `gsd-t graph cluster`
24
+ for the prd phase (when a graph index exists) and injects the pre-computed coupling clusters
25
+ into the agent context. Use this slice to organize PRD requirements around real structural
26
+ boundaries — the cluster output reveals natural product areas aligned with codebase coupling,
27
+ making PRD sections directly traceable to domain boundaries.
28
+
29
+ **On `graph-unavailable` (existing codebase):** the phase Workflow surfaces a LOUD message
30
+ and the PRD agent FAILS LOUD — it does NOT silently fall back to LLM-estimated structure.
31
+ Run `gsd-t graph status` to diagnose. For greenfield projects, graph-unavailable is expected.
32
+
33
+ Graph consumer manifest row: `commands/gsd-t-prd.md | templates/workflows/gsd-t-phase.workflow.js | reader | cluster | LLM-estimated structure for PRD decomposition`
34
+
17
35
  ## Step 2: Resolve the active model profile (M86 — invoke-time injection)
18
36
 
19
37
  Before calling the Workflow, resolve the active model profile to build the `overrides` map:
@@ -26,6 +26,25 @@ If context is thin, ask the user targeted questions:
26
26
 
27
27
  Do NOT proceed until you have enough context to make informed milestone decisions. It's better to ask 3 good questions now than to repartition later.
28
28
 
29
+ ## Step 1.5: Graph Structural Slice — cluster (M94-D10)
30
+
31
+ **[RULE] partition-project-use-cluster-verb** — when an existing codebase is present, the
32
+ project agent MUST use the graph CLI `cluster` verb to identify tightly-coupled file groups
33
+ for structure-aligned milestone decomposition, NOT LLM-reconstructed coupling.
34
+
35
+ The phase Workflow (`gsd-t-phase.workflow.js`) automatically queries `gsd-t graph cluster`
36
+ for the project phase (when a graph index exists) and injects the pre-computed coupling
37
+ clusters into the agent context. The `cluster` verb returns file groups via a DETERMINISTIC
38
+ Jaccard coupling metric — use this to identify natural milestone boundaries where coupling
39
+ is low (low-coupling boundary = good place to end one milestone and start another).
40
+
41
+ **On `graph-unavailable` (existing codebase):** the phase Workflow surfaces a LOUD message
42
+ and the project agent FAILS LOUD — it does NOT silently fall back to LLM-estimated coupling.
43
+ Run `gsd-t graph status` to diagnose. For greenfield projects with no existing codebase,
44
+ graph-unavailable is expected — the milestone decomposition proceeds from requirements only.
45
+
46
+ Graph consumer manifest row: `commands/gsd-t-project.md | templates/workflows/gsd-t-phase.workflow.js | reader | cluster | LLM-estimated coupling for milestone decomposition`
47
+
29
48
  ## Step 2: Identify the Full Scope
30
49
 
31
50
  ```bash
@@ -9,13 +9,24 @@ Read:
9
9
  2. `.gsd-t/roadmap.md` — current milestone plan
10
10
  3. `.gsd-t/progress.md` — what's in progress
11
11
 
12
- ## Step 1.5: Graph-Enhanced Impact Radius
12
+ ## Step 1.5: Graph Structural Slice — blast-radius (M94-D10)
13
13
 
14
- If `.gsd-t/graph/meta.json` exists (graph index is available):
15
- 1. For each debt item, query `getCallers` on the affected entity to calculate impact radius items with more callers have higher blast radius and may warrant higher priority
16
- 2. Include caller counts in the promotable items display to help the user prioritize
14
+ **[RULE] populate-promotedebt-prd-use-graph-not-grep** the promote-debt agent MUST use the
15
+ graph CLI `blast-radius` verb to scope a debt item's reach before milestone-izing, NOT
16
+ grep/getCallers reconstruction of the impact.
17
17
 
18
- If graph is not available, skip this step.
18
+ The phase Workflow (`gsd-t-phase.workflow.js`) automatically queries `gsd-t graph blast-radius`
19
+ for the promote-debt phase and injects the pre-computed blast-radius slice into the agent
20
+ context. Use this slice to:
21
+ 1. Calculate impact radius per debt item — items with larger blast radius have higher priority
22
+ (more downstream code is affected).
23
+ 2. Include the structural blast-radius count in the promotable items display for prioritization.
24
+
25
+ **On `graph-unavailable`:** the phase Workflow surfaces a LOUD message and the promote-debt
26
+ agent FAILS LOUD — it does NOT silently skip the impact radius calculation or fall back to grep.
27
+ Run `gsd-t graph status` to diagnose.
28
+
29
+ Graph consumer manifest row: `commands/gsd-t-promote-debt.md | templates/workflows/gsd-t-phase.workflow.js | reader | blast-radius | grep-reconstructed caller-count for debt impact scoping`
19
30
 
20
31
  ## Step 2: Identify Items to Promote
21
32
 
@@ -26,14 +26,26 @@ You are the QA Agent. You are spawned as a teammate by other GSD-T commands. You
26
26
 
27
27
  If a test requires a source code change (e.g., adding an export for testability), message the lead — do not make the change yourself.
28
28
 
29
- ## Graph-Enhanced Coverage Analysis
30
-
31
- If `.gsd-t/graph/meta.json` exists (graph index is available):
32
- 1. Query `getTestsFor` each contract entity to identify coverage gaps entities with no tests are priority targets
33
- 2. Query `findDeadCode` to flag untested dead code dead code without tests should be reported as cleanup candidates
34
- 3. Use these findings to prioritize test generation in all phases below
35
-
36
- If graph is not available, skip this step and rely on filesystem-based test discovery.
29
+ ## Graph Structural Slice — dead-code + dangling (M94-D10)
30
+
31
+ **[RULE] qa-verify-use-orphan-dangling-verbs** the QA agent MUST use the graph CLI
32
+ `dead-code` and `dangling` verbs to find dead code and dangling refs structurally, NOT
33
+ grep/raw-read reconstruction of the dead-code or dangling-ref picture.
34
+
35
+ The phase Workflow (`gsd-t-phase.workflow.js`) automatically queries `gsd-t graph dead-code`
36
+ for the qa phase and injects the pre-computed dead-code/dangling slice into the agent context.
37
+ Use this slice to:
38
+ 1. Identify coverage gaps: `dead-code` returns function entities with no live importers or
39
+ callers — entities with no tests and no live callers are priority cleanup candidates.
40
+ 2. Identify dangling refs: `dangling` returns call/import edges whose target is not in the
41
+ indexed node set (delete/rename residue).
42
+ 3. Prioritize test generation around these structural findings.
43
+
44
+ **On `graph-unavailable`:** the phase Workflow surfaces a LOUD message and the QA agent
45
+ FAILS LOUD — it does NOT silently fall back to filesystem-based test discovery for the
46
+ structural dead-code / dangling question. Run `gsd-t graph status` to diagnose.
47
+
48
+ Graph consumer manifest row: `commands/gsd-t-qa.md | templates/workflows/gsd-t-phase.workflow.js | reader | dead-code,dangling | grep/filesystem dead-code discovery for coverage gap analysis`
37
49
 
38
50
  ## Phase-Specific Behavior
39
51
 
@@ -174,6 +174,16 @@ fi
174
174
  ```
175
175
  <!-- /M56-D4: preflight + brief + verify-gate wire-in -->
176
176
 
177
+ ## Graph-Enhanced Structural Impact — WRITER Pattern (M94-D11)
178
+
179
+ Quick applies the **WRITER pattern** from `graph-consumer-wiring-contract.md`:
180
+
181
+ **READER half:** Before the task agent edits code, the quick workflow queries `blast-radius` / `who-imports` to assess the structural impact of the change — which files depend on the target, what is in the blast radius. This structural slice replaces grep/raw-read for the dependency question. The slice is injected into the task agent's context.
182
+
183
+ **WRITER half:** After edits land, the workflow triggers a re-index of the touched files (`freshness_check_on_query` from `graph-freshness-contract.md` D4 surface) so downstream graph queries see fresh edges. `[RULE] quick-writer-pattern`.
184
+
185
+ **FAIL-LOUD on graph-unavailable:** On `{ok:false, reason:"graph-unavailable"}`, the structural-impact query surfaces `"graph unavailable — fix it (gsd-t graph status)"` and the agent proceeds without the structural slice — it does NOT fall back to grep for the structural question. `[RULE] consumer-structural-grep-removed`.
186
+
177
187
  ## Step 1.5: Graph-Enhanced Scope Check
178
188
 
179
189
  ```bash
@@ -26,7 +26,16 @@ You are the lead agent. Your sole responsibility is to invoke the canonical scan
26
26
 
27
27
  ## What the Workflow does (background — NOT your to-do list)
28
28
 
29
- Replaces the legacy 5-teammate prose scan with a single deterministic, **volume-scaled** Workflow:
29
+ Replaces the legacy 5-teammate prose scan with a single deterministic, **volume-scaled**, **graph-augmented** Workflow (M94-D6):
30
+
31
+ **Graph-wiring (M94 — additive, current scan architecture kept intact):** A `Graph-Wiring` phase runs after the volume probe. When the dependency graph index is live (`gsd-t graph status` returns ok), the Workflow queries the D5 CLI for the pre-computed structural slice (dead-code candidates, dangling references, tightly-coupled file clusters) and **INJECTS** it ADDITIVELY into each deep-finder agent's context. The finders then reason over accurate, deterministic structure rather than LLM-reconstructing relationships from file reads. The `graphMode` arg controls this:
32
+ - `"wired"` (default) — build index if absent, query structural slice, inject into finders. `graphWiring.mode: "wired"` in the result.
33
+ - `"disabled"` — skip all graph calls (the no-graph baseline for AC-4 INSIGHT-delta comparison). `graphWiring.mode: "disabled"` in the result. Zero graph queries fired in this path.
34
+ - Graph unavailable → scan falls back to full grep-mode, **ANNOUNCED** (never silent). `graphWiring.mode: "fallback-announced"` in the result.
35
+
36
+ The current scan architecture (enumerate + per-file deep-finders) is **KEPT FULLY INTACT** — graph wiring is additive.
37
+
38
+
30
39
 
31
40
  ```
32
41
  preflight → volume-probe → pipeline(per-slice deep finder → single verify) → synthesis → document → render
@@ -59,7 +68,8 @@ It prints the absolute path (exit 0). Use that exact string as `scriptPath`. If
59
68
  projectDir: ".", // the project to scan
60
69
  scanNumber: 12, // optional — for the register header
61
70
  maxSlicesHint: 40, // optional — soft cap on derived slices (no silent truncation)
62
- verify: "single" // optional — "single" (default) | "none"
71
+ verify: "single", // optional — "single" (default) | "none"
72
+ graphMode: "wired" // optional — "wired" (default) | "disabled" (no-graph baseline for AC-4)
63
73
  }
64
74
  }
65
75
  ```
@@ -79,7 +89,15 @@ The Workflow returns:
79
89
  registerPath: ".gsd-t/techdebt.md",
80
90
  archivePath: ".gsd-t/techdebt_YYYY-MM-DD.md" | null,
81
91
  htmlReport: "<path>" | null,
82
- probeTotals: { files, routes, tables, components, ... }
92
+ probeTotals: { files, routes, tables, components, ... },
93
+ // M94-D6: graph wiring status — [RULE] scan-injects-structural-slice
94
+ graphWiring: {
95
+ mode: "wired" | "fallback-announced" | "disabled",
96
+ structuralSlicePresent: true | false,
97
+ deadCodeCount: 12, // dead-code candidates injected into finders
98
+ danglingCount: 3, // dangling-reference edges injected
99
+ clusterCount: 5, // coupling clusters injected
100
+ }
83
101
  }
84
102
  ```
85
103
 
@@ -32,6 +32,16 @@ Identify:
32
32
  - Naming conventions
33
33
  - Test run commands (from package.json scripts, Makefile, or CI config)
34
34
 
35
+ ## Graph-Enhanced Test Alignment — WRITER Pattern (M94-D11)
36
+
37
+ Test-sync applies the **WRITER pattern** from `graph-consumer-wiring-contract.md`:
38
+
39
+ **READER half (test-impl verb):** Use `gsd-t graph test-impl` to identify which impl functions a test exercises (from call-site edges) and `gsd-t graph test-impl --inverse` (untested-impl mode) to find impl functions with no test-file caller. This replaces grep/filesystem discovery (`find tests/ -name "*{module_name}*"`) for the structural test↔impl mapping question. The test-impl slice is injected into the test-sync agent's context for alignment decisions. `[RULE] test-sync-uses-test-impl-verb`.
40
+
41
+ **WRITER half:** After writing or updating tests, trigger a re-index of the edited test files (`freshness_check_on_query` from `graph-freshness-contract.md` D4 surface) so the next `test-impl` query sees the updated call-site edges from the new test code. `[RULE] test-sync-uses-test-impl-verb`.
42
+
43
+ **FAIL-LOUD on graph-unavailable:** On `{ok:false, reason:"graph-unavailable"}`, the test-impl query surfaces `"graph unavailable — fix it (gsd-t graph status)"` and the agent proceeds with filesystem discovery as announced fallback — it does NOT silently treat a missing graph as "no coverage". `[RULE] consumer-structural-grep-removed`.
44
+
35
45
  ## Step 1.5: Graph-Enhanced Test Discovery
36
46
 
37
47
  ```bash
@@ -28,6 +28,31 @@ Per `.gsd-t/contracts/orthogonal-validation-contract.md` v1.0.0 STABLE, the thre
28
28
 
29
29
  Read `.gsd-t/progress.md` to determine the active milestone.
30
30
 
31
+ ## Step 1.5: Graph Structural Slice — dead-code + dangling (M94-D10, ADDITIVE)
32
+
33
+ **[RULE] qa-verify-use-orphan-dangling-verbs** — the verify Workflow adds `dead-code` and
34
+ `dangling` structural checks as ADDITIVE quality gates (enriching the existing triad), NOT
35
+ as a replacement for any existing gate.
36
+
37
+ **[RULE] verify-integrate-graph-additive-announced-not-hard-fail** — verify's graph query
38
+ degrades ANNOUNCED on graph-unavailable, NOT as a hard-fail of the entire verify run.
39
+
40
+ The verify Workflow (`gsd-t-verify.workflow.js`) queries `gsd-t graph dead-code` and
41
+ `gsd-t graph dangling` as part of its quality gates. These structural checks ENRICH the QA
42
+ triad member's context:
43
+ - `dead-code`: function entities with no live importers or callers (structural orphans)
44
+ - `dangling`: call/import edges whose target is not in the indexed node set (delete/rename residue)
45
+
46
+ **On `graph-unavailable`:** the verify Workflow records a WARNING and CONTINUES its other gates:
47
+ ```
48
+ ⚠ graph unavailable — structural gate skipped, fix it (gsd-t graph status)
49
+ ```
50
+ It does NOT hard-fail the verify run solely due to graph unavailability — verify must always
51
+ be able to run. This is the ONLY exception to the reader hard-stop rule (PRE-MORTEM Finding 3,
52
+ bootstrap carve-out). The verify Workflow does NOT silently grep for the structural question.
53
+
54
+ Graph consumer manifest row (lint-exempt per verify+integrate carve-out): `commands/gsd-t-verify.md | templates/workflows/gsd-t-verify.workflow.js | reader | dead-code,dangling | grep-based dead-code/dangling detection`
55
+
31
56
  ## Step 2: Resolve the active model profile (M86 — invoke-time injection)
32
57
 
33
58
  Before calling the Workflow, resolve the active model profile to build the `overrides` map:
@@ -91,6 +91,14 @@ Call the `Workflow` tool with:
91
91
 
92
92
  For milestones with multiple waves (see `.gsd-t/contracts/m{NN}-integration-points.md` "Wave Sequencing"), invoke `gsd-t-wave` once per wave. Each call uses the domain list for that wave. Checkpoints (C1, C2, C3, …) gate between waves — verify the prior wave's verdict before starting the next.
93
93
 
94
+ ## Graph-Aware Disjointness (inherited from execute — M94-D11)
95
+
96
+ Wave composes `gsd-t-execute` as a sub-workflow. The **graph-aware disjointness** invariant (M94-D11) applies through execute:
97
+ - The disjointness check uses `gsd-t graph blast-radius` and `gsd-t graph who-imports` for transitive dependency overlap (not only literal `Touches` overlap).
98
+ - On `graph-unavailable`, the disjointness check **FAILS LOUD and HALTS** — does NOT fan out on a grep-reconstructed guess. `[RULE] execute-disjointness-fail-loud-halts-never-grep-guess`.
99
+ - Bootstrap escape hatch: `--disjointness-fallback=touches-only` is available on `graph-unavailable` (but NOT on `graph-says-non-disjoint`). See `commands/gsd-t-execute.md` for the full escape-hatch protocol.
100
+ - **WRITER half:** touched files re-indexed after each domain's edits (freshness trigger per `graph-freshness-contract.md`).
101
+
94
102
  ## Document Ripple
95
103
 
96
104
  Wave delegates to execute and verify; their doc-ripple guarantees apply. Wave itself adds:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tekyzinc/gsd-t",
3
- "version": "4.9.13",
3
+ "version": "4.10.10",
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",
@@ -22,13 +22,21 @@
22
22
  "gsd-t": "bin/gsd-t.js"
23
23
  },
24
24
  "scripts": {
25
+ "pretest": "node -e \"const fs=require('fs');for(const f of ['.gsd-t/graph.db','.gsd-t/graph.db-wal','.gsd-t/graph.db-shm']){try{if(fs.existsSync(f))fs.renameSync(f,f+'.testbak')}catch{}}\"",
25
26
  "test": "node --test",
27
+ "posttest": "node -e \"const fs=require('fs');for(const f of ['.gsd-t/graph.db','.gsd-t/graph.db-wal','.gsd-t/graph.db-shm']){try{if(fs.existsSync(f+'.testbak'))fs.renameSync(f+'.testbak',f)}catch{}}\"",
26
28
  "e2e": "playwright test",
27
29
  "e2e:install": "playwright install chromium",
28
30
  "prepublishOnly": "npm test"
29
31
  },
30
32
  "devDependencies": {
31
- "@playwright/test": "^1.55.0"
33
+ "@playwright/test": "^1.55.0",
34
+ "better-sqlite3": "^12.11.1",
35
+ "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"
32
40
  },
33
41
  "files": [
34
42
  "bin/",
@@ -13,6 +13,20 @@
13
13
  // (model: cycle===1?"opus":(overrides["debug-cycle-2"]??"fable")) is PRESERVED; the research
14
14
  // stage is a SEPARATE agent() with its own bare "fable" literal. Idempotent per §4.1.
15
15
  //
16
+ // M94-D11 WRITER pattern (graph-consumer-wiring-contract.md §WRITER Pattern):
17
+ // READER half: before the fix agent, query blast-radius + who-calls to localize the
18
+ // bug's call chain (where does this symptom reach / what calls the failing function) —
19
+ // replaces grep-reconstructed call-chain discovery. The graph slice is injected into
20
+ // the debug cycle agent's context so it doesn't need to grep.
21
+ // WRITER half: after the fix lands (filesEdited), trigger a re-index of the touched
22
+ // set — the freshness_check_on_query (graph-freshness-contract.md D4) re-indexes any
23
+ // content-hash-dirty file before answering the next structural query, so downstream
24
+ // consumers see fresh edges after the debug fix.
25
+ // FAIL-LOUD invariant: on graph-unavailable the graph-query step surfaces the message
26
+ // "graph unavailable — fix it (gsd-t graph status)" and halts — it does NOT fall back
27
+ // to grep for the structural question. The existing 2-cycle debug logic is NOT disrupted.
28
+ // [RULE] debug-reader-and-writer-both
29
+ //
16
30
  // M90 §3 D4-T3: Loop-ledger halt wired via inline runCli helper (option b).
17
31
  // After each cycle, append-cycle is called with the symptom as the assertion, the primary
18
32
  // edited file as the surface, and "unit" as the fileClass. After cycle 2 (end of loop),
@@ -280,6 +294,62 @@ async function runResearchForClaim(projectDir, claimText, artifactPath, phaseNam
280
294
  }
281
295
  }
282
296
 
297
+ // M94-D11 §READER: Query blast-radius + who-calls to localize the bug's call chain.
298
+ // Returns a short text snippet injected into the debug cycle agent context.
299
+ // On graph-unavailable: surfaces the message and returns null (agent proceeds without
300
+ // the structural slice — the fail-loud requirement is met by the logged message;
301
+ // the agent is NOT given a grep fallback to find the call chain).
302
+ // [RULE] debug-reader-and-writer-both
303
+ async function queryGraphForDebug(projectDir, symptom, phaseName) {
304
+ // Derive a plausible target hint from the symptom text (the file or function name)
305
+ // to pass to blast-radius. This is heuristic — the agent reads the full slice.
306
+ const targetHint = (symptom || "").slice(0, 80).replace(/['"]/g, "").trim();
307
+ if (!targetHint) return null;
308
+
309
+ const blastResult = await runCli(
310
+ projectDir,
311
+ "graph",
312
+ ["blast-radius", targetHint],
313
+ "gsd-t-graph-query-cli.cjs",
314
+ "graph-blast-radius",
315
+ true,
316
+ phaseName
317
+ );
318
+ const env = blastResult.envelope;
319
+ if (!env) return null;
320
+ if (!env.ok && env.reason === "graph-unavailable") {
321
+ log(`M94-D11 READER: graph unavailable — fix it (gsd-t graph status). Debug proceeds without structural slice.`);
322
+ return null; // Fail-loud logged; no grep fallback
323
+ }
324
+ if (env.ok && Array.isArray(env.results)) {
325
+ return `## Graph structural slice (blast-radius for "${targetHint}"):\n${JSON.stringify(env.results.slice(0, 20), null, 2)}`;
326
+ }
327
+ return null;
328
+ }
329
+
330
+ // M94-D11 §WRITER: Trigger a freshness pass over the touched set after the fix lands.
331
+ // Calls gsd-t graph freshness (or who-imports) over each touched file so the next
332
+ // structural query sees fresh edges (graph-freshness-contract.md D4 surface).
333
+ // Best-effort (non-blocking) — failure is logged, not fatal.
334
+ // [RULE] debug-reader-and-writer-both
335
+ async function reindexTouchedFiles(projectDir, filesEdited, phaseName) {
336
+ if (!Array.isArray(filesEdited) || filesEdited.length === 0) return;
337
+ for (const f of filesEdited.slice(0, 10)) {
338
+ // who-imports triggers freshness_check_on_query on the file before answering —
339
+ // the side-effect is the re-index of f if it is content-hash-dirty.
340
+ await runCli(
341
+ projectDir,
342
+ "graph",
343
+ ["who-imports", f],
344
+ "gsd-t-graph-query-cli.cjs",
345
+ `graph-reindex-touched:${f.slice(-30)}`,
346
+ true,
347
+ phaseName
348
+ ).catch(() => {}); // best-effort; don't halt debug on re-index failure
349
+ }
350
+ log(`M94-D11 WRITER: re-indexed ${Math.min(filesEdited.length, 10)} touched file(s) after fix`);
351
+ }
352
+
283
353
  if (!symptom) {
284
354
  log("debug: args.symptom required (description of failing test or error)");
285
355
  return { status: "failed", reason: "no-symptom" };
@@ -290,6 +360,10 @@ const pre = await runPreflight(projectDir);
290
360
  if (!pre.ok) return { status: "failed", reason: "preflight-failed", preflight: pre.envelope };
291
361
  const brief = await generateBrief(projectDir, { kind: "execute", id: "debug-brief" });
292
362
 
363
+ // M94-D11 §READER: query graph ONCE before cycles (the structural slice is the same
364
+ // for all cycles — same symptom). Injected into each cycle's prompt.
365
+ const _graphSliceLine = await queryGraphForDebug(projectDir, symptom, "Preflight") || "";
366
+
293
367
  let lastResult = null;
294
368
  // M90 §3 (fix-cycle: gate lifecycle) — track THIS run's per-signature cycle counts so the
295
369
  // non-convergence halt is RUN-SCOPED + REACHABLE within the 2-cycle debug cap (not dependent on
@@ -301,6 +375,7 @@ for (let cycle = 1; cycle <= 2; cycle++) {
301
375
  const prompt = [
302
376
  `Debug cycle ${cycle} of 2. Symptom: ${symptom}`,
303
377
  `**Brief:** ${brief.briefPath || "(no brief)"}`,
378
+ _graphSliceLine ? `\n**Graph structural slice (M94-D11 READER — blast-radius/who-calls localization):**\n${_graphSliceLine}\nUse this slice to localize the call chain — do NOT grep for structural relationships.` : "",
304
379
  cycle > 1 && lastResult
305
380
  ? `\nPREVIOUS CYCLE'S ROOT CAUSE HYPOTHESIS (did not resolve the issue):\n${lastResult.rootCause}\nFiles edited: ${lastResult.filesEdited.join(", ")}\nIf the hypothesis was right, the fix was incomplete. If wrong, formulate a different hypothesis.`
306
381
  : "",
@@ -330,6 +405,12 @@ for (let cycle = 1; cycle <= 2; cycle++) {
330
405
  nextStepsIfNotResolved: "agent threw — investigate directly",
331
406
  }));
332
407
 
408
+ // M94-D11 §WRITER: re-index touched files after fix lands (freshness trigger).
409
+ // If the fix edited files, trigger a freshness pass so the next graph query sees fresh edges.
410
+ if (Array.isArray(lastResult.filesEdited) && lastResult.filesEdited.length > 0) {
411
+ await reindexTouchedFiles(projectDir, lastResult.filesEdited, `Cycle ${cycle}`);
412
+ }
413
+
333
414
  // M89 Research — classify GUESSED claims from this debug cycle (§6.5 trigger, separate from debug-cycle ternary)
334
415
  if (Array.isArray(lastResult.statedClaims) && lastResult.statedClaims.length > 0) {
335
416
  for (const claimLine of lastResult.statedClaims) {
@@ -72,11 +72,57 @@ const pre = await runPreflight(projectDir);
72
72
  if (!pre.ok) return { status: "failed", reason: "preflight-failed", preflight: pre.envelope };
73
73
  const brief = await generateBrief(projectDir, { kind: "execute", milestone, id: `integrate-${(milestone || "m").toLowerCase()}` });
74
74
 
75
+ // M94-D10-T6: Graph Structural Slice — who-imports + blast-radius (ADDITIVE, announced-degradation)
76
+ // [RULE] integrate-uses-graph-for-wiring-verification
77
+ // [RULE] verify-integrate-graph-additive-announced-not-hard-fail — bootstrap carve-out:
78
+ // integrate degrades ANNOUNCED on graph-unavailable, does NOT hard-fail.
79
+ let _graphWhoImportsSlice = null;
80
+ let _graphBlastRadiusSlice = null;
81
+ let _graphIntegrateWarning = null;
82
+
83
+ {
84
+ const wiResult = await runCli(
85
+ projectDir, "graph who-imports", [], "gsd-t-graph-query-cli.cjs",
86
+ "graph:who-imports", true, "Integrate"
87
+ );
88
+ const wiEnv = wiResult.envelope || {};
89
+ if (wiEnv.ok === true) {
90
+ _graphWhoImportsSlice = wiEnv;
91
+ log(`M94 graph who-imports: ${(wiEnv.results || []).length} result(s) (tier: ${wiEnv.tier || "?"})`);
92
+ } else if (wiEnv.reason === "graph-unavailable") {
93
+ _graphIntegrateWarning = "⚠ graph unavailable — structural wiring-check skipped, fix it (gsd-t graph status)";
94
+ log(`M94 graph who-imports: ${_graphIntegrateWarning}`);
95
+ } else {
96
+ _graphIntegrateWarning = `⚠ graph who-imports query unexpected envelope (reason: ${wiEnv.reason || "?"}); structural wiring-check skipped`;
97
+ log(`M94 graph who-imports: ${_graphIntegrateWarning}`);
98
+ }
99
+
100
+ if (!_graphIntegrateWarning) {
101
+ const brResult = await runCli(
102
+ projectDir, "graph blast-radius", [], "gsd-t-graph-query-cli.cjs",
103
+ "graph:blast-radius", true, "Integrate"
104
+ );
105
+ const brEnv = brResult.envelope || {};
106
+ if (brEnv.ok === true) {
107
+ _graphBlastRadiusSlice = brEnv;
108
+ log(`M94 graph blast-radius: ${(brEnv.results || []).length} result(s)`);
109
+ } else {
110
+ log(`M94 graph blast-radius: query failed (reason: ${brEnv.reason || "?"}) — no blast-radius slice`);
111
+ }
112
+ }
113
+ }
114
+
75
115
  phase("Integrate");
76
116
  const integrate = await agent(
77
117
  [
78
118
  `You are the integration agent for milestone \`${milestone}\`. Domains complete: ${domains.join(", ")}.`,
79
119
  `**Brief:** ${brief.briefPath || "(no brief — re-walk repo)"}`,
120
+ // M94-D10-T6: thread graph slice into the integrate agent (ADDITIVE)
121
+ _graphIntegrateWarning
122
+ ? `\n**Graph Structural Check:** ${_graphIntegrateWarning} (WARNING only — integrate continues cross-domain wire-up)`
123
+ : (_graphWhoImportsSlice
124
+ ? `\n**Graph Structural Slice (who-imports):** ${JSON.stringify(_graphWhoImportsSlice)}\n**Graph Structural Slice (blast-radius):** ${_graphBlastRadiusSlice ? JSON.stringify(_graphBlastRadiusSlice) : "N/A"}\nUse these pre-computed slices to verify cross-domain wiring — that real import/call edges exist across domain seams. Do NOT grep/read-to-reconstruct cross-domain edges; the graph slice above is authoritative.`
125
+ : ""),
80
126
  ``,
81
127
  `Read .gsd-t/contracts/${milestone ? milestone.toLowerCase() : ""}-integration-points.md if present.`,
82
128
  `Resolve any shared-file edits sequenced at integrate (per "Cross-Domain File Contention Matrix").`,
@@ -84,7 +130,7 @@ const integrate = await agent(
84
130
  `Commit cross-domain edits with a clear "m61(integrate)" prefix.`,
85
131
  ``,
86
132
  `Return JSON per the schema.`,
87
- ].join("\n"),
133
+ ].filter(Boolean).join("\n"),
88
134
  { label: "integrate", phase: "Integrate", schema: INTEGRATE_SCHEMA, model: "sonnet" }
89
135
  ).catch((e) => ({ status: "failed", crossDomainEdits: [], notes: `agent error: ${e && e.message}` }));
90
136