@tekyzinc/gsd-t 4.9.14 → 4.10.11
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 +30 -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 +622 -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-require-store.cjs +89 -0
- package/bin/gsd-t-scip-reader.cjs +167 -0
- package/bin/gsd-t.js +170 -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 +12 -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 +84 -0
- 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
|
@@ -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:
|
package/commands/gsd-t-plan.md
CHANGED
|
@@ -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-
|
|
33
|
+
## Graph Structural Slice — who-imports + cluster (M94-D10)
|
|
34
34
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
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"
|
package/commands/gsd-t-prd.md
CHANGED
|
@@ -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-
|
|
12
|
+
## Step 1.5: Graph Structural Slice — blast-radius (M94-D10)
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
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
|
|
package/commands/gsd-t-qa.md
CHANGED
|
@@ -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-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
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
|
|
package/commands/gsd-t-quick.md
CHANGED
|
@@ -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
|
package/commands/gsd-t-scan.md
CHANGED
|
@@ -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"
|
|
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
|
package/commands/gsd-t-verify.md
CHANGED
|
@@ -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:
|
package/commands/gsd-t-wave.md
CHANGED
|
@@ -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.
|
|
3
|
+
"version": "4.10.11",
|
|
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,17 @@
|
|
|
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
|
+
"graphology": "^0.26.0",
|
|
35
|
+
"kuzu": "^0.11.3"
|
|
32
36
|
},
|
|
33
37
|
"files": [
|
|
34
38
|
"bin/",
|
|
@@ -41,5 +45,11 @@
|
|
|
41
45
|
],
|
|
42
46
|
"engines": {
|
|
43
47
|
"node": ">=16.0.0"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"better-sqlite3": "^12.11.1",
|
|
51
|
+
"tree-sitter": "^0.21.1",
|
|
52
|
+
"tree-sitter-typescript": "^0.23.2",
|
|
53
|
+
"tree-sitter-python": "^0.21.0"
|
|
44
54
|
}
|
|
45
55
|
}
|
|
@@ -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
|
|