@tekyzinc/gsd-t 4.17.10 → 4.18.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 +12 -0
- package/README.md +1 -1
- package/commands/gsd-t-init-scan-setup.md +73 -12
- package/commands/gsd-t-scan.md +1 -1
- package/package.json +1 -1
- package/templates/workflows/gsd-t-scan.workflow.js +133 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to GSD-T are documented here. Updated with each release.
|
|
4
4
|
|
|
5
|
+
## [4.18.10] - 2026-07-03
|
|
6
|
+
|
|
7
|
+
### Added — Scan Consolidation Opportunities + type-grouped register; init-scan-setup repo adoption
|
|
8
|
+
|
|
9
|
+
The scan register is now organized by TD **type within each severity**, and a new **Consolidation** phase (opus + graph-assisted) appends a **🧩 Consolidation Opportunities** section listing high-confidence clusters of findings that share a root and should be fixed as one workstream. `/gsd-t-init-scan-setup` gains a **Step 0 "Adopt a Foreign Repo"** sanitization pass and a **Step 4a completeness check**, so repos built by others (or plain Claude) run correctly on this machine and an incomplete/hand-driven scan is caught and re-run.
|
|
10
|
+
|
|
11
|
+
- `templates/workflows/gsd-t-scan.workflow.js`: register `fmtChunks` groups by `typeOf(area)` within severity via a shared `orderedFindings` ordering (so consolidation TD-N ids match the register exactly); new `Consolidation` phase (opus, graph-assisted via `who-imports`/`who-calls`/`blast-radius`) emits `CG-N` groups (members/sharedRoot/recommendedAction/effort) appended at the end; `meta.phases` +1. Best-effort — any failure leaves the register intact.
|
|
12
|
+
- `commands/gsd-t-init-scan-setup.md`: Step 0 adoption (settings reconciled vs global, CLAUDE.md regenerated from fresh scan, no foreign/stale workflow-script execution) + Step 4a post-scan completeness check + 8-phase scan description + corrected graph note (`graphDB/`).
|
|
13
|
+
- `commands/gsd-t-scan.md`: synthesis/consolidation phase description.
|
|
14
|
+
- `.gsd-t/backlog.md`: split logging item #48 → #48 (trace) + #49 (audit) with full specs.
|
|
15
|
+
- Lints green: M71 runtime-native, M85 tier-policy, M76 ascii. Suite 2631/2631.
|
|
16
|
+
|
|
5
17
|
## [4.17.10] - 2026-07-01
|
|
6
18
|
|
|
7
19
|
### Changed — `/gsd-t-estimate` v2: generalized + supervised + adversarial, with a permanent Sheets service account
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# GSD-T: Contract-Driven Development for Claude Code
|
|
2
2
|
|
|
3
|
-
**v4.
|
|
3
|
+
**v4.18.10** - A methodology for reliable, parallelizable development using Claude Code with optional Agent Teams support.
|
|
4
4
|
|
|
5
5
|
**Eliminates context rot** — task-level fresh dispatch (one subagent per task, ~10-20% context each) means compaction never triggers.
|
|
6
6
|
**Compaction-proof debug loops** — `gsd-t headless --debug-loop` runs test-fix-retest cycles as separate `claude -p` sessions. A JSONL debug ledger persists all hypothesis/fix/learning history across fresh sessions. Anti-repetition preamble injection prevents retrying failed hypotheses. Escalation tiers (sonnet → opus → human) and a hard iteration ceiling enforced externally.
|
|
@@ -4,6 +4,48 @@ One command to fully onboard a project into GSD-T. Combines project directory se
|
|
|
4
4
|
|
|
5
5
|
Can be run from anywhere — does not require being in the project folder first.
|
|
6
6
|
|
|
7
|
+
## Entry States (READ FIRST — the command is IDEMPOTENT and SAFE to re-run)
|
|
8
|
+
|
|
9
|
+
This command behaves **identically and unambiguously** across all three entry states:
|
|
10
|
+
|
|
11
|
+
- **(A) Greenfield** — brand-new project, no code, no `.gsd-t/`.
|
|
12
|
+
- **(B) Existing, new to this machine** — has code and/or a `.gsd-t/` dir but is not in this machine's registry.
|
|
13
|
+
- **(C) Already-registered, being re-run.**
|
|
14
|
+
|
|
15
|
+
**Existing `.gsd-t/` artifacts are NEVER a reason to skip scan or setup.** The ONLY thing "already exists" skips is **re-creating directory structure** (Step 3). Steps 4 (scan) and 5 (setup) **ALWAYS run**, on every invocation, in every entry state.
|
|
16
|
+
|
|
17
|
+
**Re-running is expected and safe.** Archiving an existing `techdebt.md` and regenerating scan docs + CLAUDE.md is the **designed behavior** of this command — it is NOT a destructive action and does NOT require user approval. Stale scan docs from a prior milestone are the exact failure this command exists to prevent. Never stop to ask permission for the archive/regenerate steps.
|
|
18
|
+
|
|
19
|
+
## Step 0: Adopt a Foreign Repo — Sanitize Inherited Context (MANDATORY, runs FIRST)
|
|
20
|
+
|
|
21
|
+
**Why:** projects are frequently imported that were built by others — with plain Claude Code, or with GSD-T on a *different* machine/context. Such a repo arrives carrying **foreign context**: a committed `.claude/settings.json`, a `CLAUDE.md` authored in another environment, and `.gsd-t/` state / git history from a prior GSD-T install. That inherited context leaks and makes the project behave unexpectedly on THIS machine (e.g. an agent hand-authoring a scan from a foreign session's pattern instead of invoking the installed Workflow). **Adoption severs that** so the repo runs the way THIS machine's GSD-T expects — independent of whoever built it.
|
|
22
|
+
|
|
23
|
+
Detect a foreign repo when ANY of these is true: the git remote is not the operator's own, the commit history shows GSD-T/Claude workflow commits (`milestone(...)`, `feat(...task-N)`, `chore(scan)`), there is a committed in-repo settings file, or `.gsd-t/` exists but the project is not in this machine's registry (entry state B).
|
|
24
|
+
|
|
25
|
+
### 0a. Settings reconciliation (in-repo settings vs GLOBAL — global is source of truth)
|
|
26
|
+
|
|
27
|
+
For each in-repo settings file (`.claude/settings.json`, `.claude/settings.local.json`, or any `settings*.json` committed in the repo tree — NOT `~/.claude/` global files):
|
|
28
|
+
|
|
29
|
+
- **Compare every entry against the operator's GLOBAL settings** (`~/.claude/settings.json` + `~/.claude/settings.local.json`).
|
|
30
|
+
- **Any permission the global ALREADY grants or denies → global wins → DELETE that entry** (or the whole file if all its content is global-governed). No approval needed — global is the single source of truth for anything it covers. The project always falls back to global.
|
|
31
|
+
- **Project-distinct content the global says NOTHING about** (a permission/env/setting unrelated to global) → **do NOT delete. HALT onboarding and SURFACE it to the operator**: show the exact content, explain it's distinct from global, and ask keep-or-discard. **Nothing proceeds past this point until the operator decides** — do not run scan/setup on an unresolved settings question.
|
|
32
|
+
- Log every deletion and every surfaced item.
|
|
33
|
+
|
|
34
|
+
### 0b. CLAUDE.md — always regenerate from the fresh scan
|
|
35
|
+
|
|
36
|
+
The imported `CLAUDE.md` may have been authored in a different project's context and cannot be trusted as current — **even if it looks correct.** Adoption does NOT try to judge foreign-vs-fine. Instead:
|
|
37
|
+
|
|
38
|
+
- **Back up** the imported file to `CLAUDE.md.imported.bak` (lossless — nothing is destroyed).
|
|
39
|
+
- **Always regenerate** the project `CLAUDE.md` from the NEW scan findings in Step 5. (The regenerate is deferred to Step 5 where the fresh scan exists; Step 0b only takes the backup + marks it for regeneration so Step 5 does not skip it.)
|
|
40
|
+
|
|
41
|
+
### 0c. Foreign `.gsd-t/` + workflow residue
|
|
42
|
+
|
|
43
|
+
- Existing `.gsd-t/` state is REFRESHED by the normal flow (Step 4 re-scan archives + regenerates), not trusted as-is.
|
|
44
|
+
- **Never run a scan from a script found inside the repo or a prior session** — always resolve `gsd-t workflow-path scan` and invoke the installed Workflow (Step 4). A repo-committed `*.workflow.js` is foreign residue; do not execute it.
|
|
45
|
+
- Register the project by FULL ABSOLUTE PATH on this machine (Step 3).
|
|
46
|
+
|
|
47
|
+
**Adoption is idempotent + safe:** on a repo already adopted, 0a finds nothing to reconcile, 0b re-backs-up only if the file changed, 0c is a no-op. It never blocks a clean project.
|
|
48
|
+
|
|
7
49
|
## Step 1: Project Directory
|
|
8
50
|
|
|
9
51
|
### 1a. Resolve the base projects directory (for new projects)
|
|
@@ -93,33 +135,48 @@ Execute the full init workflow (same as `/gsd-t-init`):
|
|
|
93
135
|
7. Initialize backlog with auto-derived categories
|
|
94
136
|
8. Register project in `~/.claude/.gsd-t-projects`
|
|
95
137
|
|
|
96
|
-
**
|
|
138
|
+
**Registry check — match by FULL ABSOLUTE PATH, not bare project name.** The registry stores full paths; a bare-name grep produces a false "not registered" (two projects can share a basename) and erodes trust in the rest of the detected state. A missing entry means "register it now" — it must NOT cast doubt on any other detected state. If already present, log and continue silently.
|
|
139
|
+
|
|
140
|
+
**If `.gsd-t/` already exists**: Skip **ONLY the dir-structure creation** that already exists — it's already done. **This skip does NOT extend to Step 4 (scan) or Step 5 (setup), which ALWAYS run.** Always ensure `settings.local` (step 5 above) and the registry entry regardless of whether init dir-creation was skipped.
|
|
97
141
|
|
|
98
142
|
## Step 3.5: Graph Indexing Note
|
|
99
143
|
|
|
100
|
-
If the project has source code,
|
|
144
|
+
If the project has source code, the **graph index is built automatically inside the scan's Graph-Wiring phase (Step 4)** — the scan builds the index if it is absent, then queries it. The store lives at `.gsd-t/graphDB/` (consolidated) — the graph provides code-level queries (callers, callees, domain ownership, dead-code detection) that enhance all subsequent GSD-T commands. **No separate indexing step is needed — the scan handles it.** (You can also build/refresh the index on demand with `gsd-t graph index`, but the scan does it for you.)
|
|
101
145
|
|
|
102
146
|
## Step 4: Deep Codebase Scan (gsd-t-scan)
|
|
103
147
|
|
|
104
|
-
|
|
148
|
+
**Invoke the PACKAGED scan Workflow — never hand-drive it.** Resolve the absolute path with `gsd-t workflow-path scan`, then call the `Workflow` tool with that `scriptPath`. **Do NOT author your own scan script, do NOT run finders/probe/synthesis by hand, and do NOT execute any `*.workflow.js` found inside the repo or a prior session** — a hand-driven or foreign scan skips phases (Graph-Wiring, Document, Plain-English) and produces an incomplete result. This is the exact failure mode observed on imported repos.
|
|
149
|
+
|
|
150
|
+
The packaged Workflow runs **8 phases**: Preflight → Probe → **Graph-Wiring** → Deep Scan → Synthesis → **Consolidation** → **Document** → **Plain-English**. It:
|
|
105
151
|
|
|
106
|
-
1.
|
|
107
|
-
2.
|
|
108
|
-
3.
|
|
109
|
-
4.
|
|
152
|
+
1. Volume-probes and slices the codebase by area; per-slice deep finders enumerate; a single verify pass drops false positives.
|
|
153
|
+
2. **Graph-Wiring** — builds the graph index if absent, then queries a structural slice (dead-code / dangling / cluster) injected additively into the finders.
|
|
154
|
+
3. Synthesis builds `.gsd-t/techdebt.md` + the `.gsd-t/scan/*.md` analysis files.
|
|
155
|
+
4. Document cross-populates living docs (architecture/workflows/infrastructure/requirements) + README.
|
|
156
|
+
5. Plain-English writes the non-technical companion.
|
|
110
157
|
|
|
111
158
|
The Workflow's fan-out scales with codebase volume automatically — no team-mode toggle.
|
|
112
159
|
|
|
113
|
-
**
|
|
160
|
+
**ALWAYS run the scan on every invocation, even on a fully-onboarded project.** Existing `techdebt.md` is archived to `techdebt_YYYY-MM-DD.md` and the scan regenerates it (TD numbering continues). **NEVER skip the scan because `.gsd-t/` or `scan/*.md` already exist — stale scan docs are the exact failure this command prevents.** Archiving is designed behavior, NOT a destructive action, and does NOT require user approval.
|
|
161
|
+
|
|
162
|
+
### 4a. Post-scan completeness check (catches a hand-driven / incomplete scan)
|
|
163
|
+
|
|
164
|
+
After the scan returns, VERIFY the packaged 7-phase Workflow actually ran by confirming its expected outputs exist and are fresh (mtime within this run):
|
|
165
|
+
|
|
166
|
+
- `.gsd-t/techdebt.md` (regenerated) **and** at least the 5 `.gsd-t/scan/*.md` dimension files
|
|
167
|
+
- the merged living docs (architecture/workflows/infrastructure/requirements touched this run)
|
|
168
|
+
- the plain-English companion (`.gsd-t/techdebt_in_plain_english.md` or equivalent)
|
|
169
|
+
|
|
170
|
+
**If any are missing → LOUD failure, do not proceed:** "⛔ Incomplete scan — the packaged Workflow's Document / Plain-English / Graph-Wiring outputs are absent. This means the scan was hand-driven or a foreign/stale script ran instead of the installed Workflow. Re-run: resolve `gsd-t workflow-path scan` and invoke the `Workflow` tool." Then re-invoke correctly, once.
|
|
114
171
|
|
|
115
172
|
## Step 5: Generate Project CLAUDE.md (gsd-t-setup)
|
|
116
173
|
|
|
117
|
-
Execute the full setup workflow (same as `/gsd-t-setup`)
|
|
174
|
+
Execute the full setup workflow (same as `/gsd-t-setup`). **ALWAYS run — regenerate/refresh the project CLAUDE.md from the NEW scan findings; do NOT assume an existing CLAUDE.md is current.** If Step 0b flagged an imported CLAUDE.md (backed up to `CLAUDE.md.imported.bak`), this regeneration is what replaces it with a machine-correct version — do NOT skip it.
|
|
118
175
|
|
|
119
176
|
1. Read global `~/.claude/CLAUDE.md` to understand what's already covered
|
|
120
|
-
2. Use scan findings + auto-detection to populate project-specific sections
|
|
177
|
+
2. Use the **fresh** scan findings + auto-detection to populate project-specific sections
|
|
121
178
|
3. Remove any global duplicates from the project CLAUDE.md
|
|
122
|
-
4. Generate and write the optimized CLAUDE.md
|
|
179
|
+
4. Generate and write the optimized CLAUDE.md (refresh the existing one — don't skip because a CLAUDE.md already exists)
|
|
123
180
|
|
|
124
181
|
At Level 3: skip questions that were auto-detected — only ask what's truly unknown.
|
|
125
182
|
At Level 1-2: ask all targeted questions per the setup workflow.
|
|
@@ -136,7 +193,11 @@ Project Onboarded: {project name}
|
|
|
136
193
|
Scan: {N} tech debt items ({critical} critical, {high} high, {medium} medium, {low} low)
|
|
137
194
|
Setup: CLAUDE.md generated ({N} sections)
|
|
138
195
|
Registry: {registered | already registered}
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
For an already-onboarded project (entry state B/C), the report reads as **"re-scanned and refreshed,"** never "nothing to do": show the archived `techdebt_YYYY-MM-DD.md` path, the fresh finding counts, and that CLAUDE.md was regenerated.
|
|
139
199
|
|
|
200
|
+
```
|
|
140
201
|
Next steps:
|
|
141
202
|
→ Review .gsd-t/techdebt.md for critical items
|
|
142
203
|
→ /gsd-t-milestone to define your first milestone
|
|
@@ -145,7 +206,7 @@ Next steps:
|
|
|
145
206
|
|
|
146
207
|
### Autonomy Behavior
|
|
147
208
|
|
|
148
|
-
**Level 3 (Full Auto)**: Run all steps without pausing. Only stop if git remote is needed (requires user input) or if a scan reveals critical security blockers.
|
|
209
|
+
**Level 3 (Full Auto)**: Run all steps without pausing. Only stop if git remote is needed (requires user input) or if a scan reveals critical security blockers. **"Already onboarded" NEVER means "nothing to do" — it means "re-scan and refresh."** Do NOT pause to ask permission for the archive/regenerate steps; re-running is expected and safe.
|
|
149
210
|
|
|
150
211
|
**Level 1-2**: Pause after each major step (init, scan, setup) for user review before continuing.
|
|
151
212
|
|
package/commands/gsd-t-scan.md
CHANGED
|
@@ -41,7 +41,7 @@ The current scan architecture (enumerate + per-file deep-finders) is **KEPT FULL
|
|
|
41
41
|
preflight → volume-probe → pipeline(per-slice deep finder → single verify) → synthesis → document → render
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
-
**Why volume-scaled (M66):** the legacy scan hard-coded exactly 5 teammates (one per dimension) with zero volume scaling — a 5-file repo and a 1,809-file repo both got 5 agents, so a single `quality` agent sampled the top ~5 issues across the whole codebase and stopped. The volume-probe stage now measures the codebase (files, routes, ORM tables, components, top-level dirs) and carves it into **narrow slices** — one deep-finder agent per area, scaling from 1-3 slices on a tiny repo to 15-40 on a large one. Each finder OWNS its slice and is mandated to *enumerate, not sample*. A single verify pass confirms each finding against the real code and drops false positives. Synthesis dedups/merges/re-ranks into `techdebt.md` and continues TD numbering from the archived prior register.
|
|
44
|
+
**Why volume-scaled (M66):** the legacy scan hard-coded exactly 5 teammates (one per dimension) with zero volume scaling — a 5-file repo and a 1,809-file repo both got 5 agents, so a single `quality` agent sampled the top ~5 issues across the whole codebase and stopped. The volume-probe stage now measures the codebase (files, routes, ORM tables, components, top-level dirs) and carves it into **narrow slices** — one deep-finder agent per area, scaling from 1-3 slices on a tiny repo to 15-40 on a large one. Each finder OWNS its slice and is mandated to *enumerate, not sample*. A single verify pass confirms each finding against the real code and drops false positives. Synthesis dedups/merges/re-ranks into `techdebt.md` (items **grouped by type within each severity** — Security/Vulnerability, Dead Code, Duplication, etc.) and continues TD numbering from the archived prior register. A **Consolidation** phase (opus + graph-assisted) then appends a **🧩 Consolidation Opportunities** section at the END of the register: high-confidence clusters of TDs that share a root and should be fixed as ONE workstream (duplicate-function families, batch dead-code deletions, a guard missing across N sites), each a candidate for a single consolidation milestone.
|
|
45
45
|
|
|
46
46
|
**Deep document stage (M67):** after the register lands, a `document` phase fans out **one agent per document**, each drawing on the same slices + verified findings, to deterministically produce the full living-document set — `docs/architecture.md` (component map per feature-domain slice), `docs/workflows.md` (a user journey per slice), `docs/infrastructure.md`, `docs/requirements.md`, `README.md` (all merge-not-overwrite) — plus the five `.gsd-t/scan/*.md` dimension files (`architecture`, `security`, `quality`, `business-rules`, `contract-drift`) in the renderer's parsed formats. This replaces M66's non-deterministic "lead-agent follow-on" so the documents are as thorough as the register. The deterministic `bin/scan-*.js` renderers (schema extraction, diagrams, HTML report) run last, reading the deep `.gsd-t/scan/architecture.md`.
|
|
47
47
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tekyzinc/gsd-t",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.18.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",
|
|
@@ -34,7 +34,8 @@ export const meta = {
|
|
|
34
34
|
{ title: "Probe", detail: "volume probe → per-area slice list", model: "sonnet" },
|
|
35
35
|
{ title: "Graph-Wiring", detail: "M94-D6: build index if absent + query structural slice (dead-code/dangling/cluster) → inject into finders ADDITIVELY; fallback announced on graph-unavailable", model: "haiku" },
|
|
36
36
|
{ title: "Deep Scan", detail: "pipeline: per-slice deep finder (graph-augmented when wired) → single verify" },
|
|
37
|
-
{ title: "Synthesis", detail: "archive prior + write fresh register + git", model: "opus" },
|
|
37
|
+
{ title: "Synthesis", detail: "archive prior + write fresh register (type-grouped within severity) + git", model: "opus" },
|
|
38
|
+
{ title: "Consolidation", detail: "opus + graph-assisted: high-confidence clusters of TDs to fix as one workstream → appended to register", model: "opus" },
|
|
38
39
|
{ title: "Document", detail: "living docs + 5 dimension files (per-doc fan-out)" },
|
|
39
40
|
{ title: "Plain-English", detail: "non-technical companion: batched gen + severity-grouped chunked write" },
|
|
40
41
|
],
|
|
@@ -785,6 +786,35 @@ function ascii(s) {
|
|
|
785
786
|
.replace(/…/g, "...") // ellipsis
|
|
786
787
|
.replace(/[ \t]+\n/g, "\n"); // tidy trailing whitespace
|
|
787
788
|
}
|
|
789
|
+
|
|
790
|
+
// Register ordering — SINGLE source of truth so the formatter's TD-numbering and the
|
|
791
|
+
// consolidation stage's TD references stay identical. Within each severity, findings
|
|
792
|
+
// are sub-grouped by TYPE (derived from `area`). typeOf() maps free-text area→bucket;
|
|
793
|
+
// unmatched → "Other". `orderedFindings` is the final, numbered sequence (TD-tdStart..).
|
|
794
|
+
function typeOf(f) {
|
|
795
|
+
const a = String(f.area || "").toLowerCase() + " " + String(f.title || "").toLowerCase();
|
|
796
|
+
if (/(vuln|idor|auth|injection|xss|csrf|secret|privilege|tenant|security|rce|ssrf)/.test(a)) return "Security / Vulnerability";
|
|
797
|
+
if (/(dead.?code|unreachable|unused|orphan|never (called|used|imported))/.test(a)) return "Dead Code";
|
|
798
|
+
if (/(duplicat|redundant|reimplement|copy)/.test(a)) return "Duplication";
|
|
799
|
+
if (/(data.?integrity|race|concurren|transaction|idempoten|double|constraint)/.test(a)) return "Data Integrity / Concurrency";
|
|
800
|
+
if (/(perf|n\+1|slow|latency|batch|query.?count)/.test(a)) return "Performance";
|
|
801
|
+
if (/(contract.?drift|schema.?drift|api.?mismatch|404|endpoint.*(missing|not exist))/.test(a)) return "Contract Drift";
|
|
802
|
+
if (/(test|coverage|shallow|flaky)/.test(a)) return "Testing";
|
|
803
|
+
return "Other";
|
|
804
|
+
}
|
|
805
|
+
const TYPE_ORDER = ["Security / Vulnerability", "Dead Code", "Duplication", "Data Integrity / Concurrency", "Performance", "Contract Drift", "Testing", "Other"];
|
|
806
|
+
const SEV_ORDER2 = { CRITICAL: 0, HIGH: 1, MEDIUM: 2, LOW: 3 };
|
|
807
|
+
// Stable sort: severity, then type, then original index. Computed ONCE; both consumers use it.
|
|
808
|
+
const orderedFindings = finalFindings
|
|
809
|
+
.map((f, i) => ({ f, i, t: typeOf(f) }))
|
|
810
|
+
.sort((a, b) => {
|
|
811
|
+
const sv = (SEV_ORDER2[a.f.severity] ?? 9) - (SEV_ORDER2[b.f.severity] ?? 9);
|
|
812
|
+
if (sv !== 0) return sv;
|
|
813
|
+
const tv = TYPE_ORDER.indexOf(a.t) - TYPE_ORDER.indexOf(b.t);
|
|
814
|
+
if (tv !== 0) return tv;
|
|
815
|
+
return a.i - b.i;
|
|
816
|
+
});
|
|
817
|
+
|
|
788
818
|
function fmtChunks(today) {
|
|
789
819
|
const sevHead = { CRITICAL: "🔴 Critical", HIGH: "🟠 High", MEDIUM: "🟡 Medium", LOW: "🟢 Low" };
|
|
790
820
|
const head = [];
|
|
@@ -820,11 +850,17 @@ function fmtChunks(today) {
|
|
|
820
850
|
|
|
821
851
|
const CHUNK_MAX = 30000;
|
|
822
852
|
const chunks = [head.join("\n")];
|
|
823
|
-
let buf = "", n = tdStart, lastSev = null;
|
|
853
|
+
let buf = "", n = tdStart, lastSev = null, lastType = null;
|
|
824
854
|
const flush = () => { if (buf) { chunks.push(buf); buf = ""; } };
|
|
825
|
-
|
|
855
|
+
// Consume the shared `orderedFindings` (severity → type → original-index) so the
|
|
856
|
+
// TD numbers assigned here are IDENTICAL to those the consolidation stage references.
|
|
857
|
+
for (const { f, t } of orderedFindings) {
|
|
826
858
|
let piece = "";
|
|
827
|
-
if (f.severity !== lastSev) { piece += `\n## ${sevHead[f.severity] || f.severity} Priority\n\n`; lastSev = f.severity; }
|
|
859
|
+
if (f.severity !== lastSev) { piece += `\n## ${sevHead[f.severity] || f.severity} Priority\n\n`; lastSev = f.severity; lastType = null; }
|
|
860
|
+
// Type sub-heading uses a bold marker line (NOT `###`) so it never collides with
|
|
861
|
+
// the `### TD-N` item headings that downstream tools grep for. ASCII hyphens only
|
|
862
|
+
// (M76: no em/en-dashes in fmtChunks literals).
|
|
863
|
+
if (t !== lastType) { piece += `**-- ${t} --**\n\n`; lastType = t; }
|
|
828
864
|
piece += itemMd(f, n++);
|
|
829
865
|
if (buf.length + piece.length > CHUNK_MAX) flush();
|
|
830
866
|
buf += piece;
|
|
@@ -889,6 +925,99 @@ log(`register written in ${chunkOk}/${chunks.length} chunks (${counts.total} fin
|
|
|
889
925
|
const synthesis = { status: "written", counts, archivePath, tdRange: `TD-${tdStart}..TD-${lastTd}`, finalFindings };
|
|
890
926
|
log(`register written: ${JSON.stringify(synthesis.counts)} (${synthesis.tdRange})`);
|
|
891
927
|
|
|
928
|
+
// ── Consolidation Opportunities (opus + graph-assisted) ────────────────────────
|
|
929
|
+
// After the register is written (TD numbers final), cluster HIGH-CONFIDENCE groups
|
|
930
|
+
// of TDs that share a root and should be fixed as ONE workstream (duplicate-function
|
|
931
|
+
// families, batch dead-code deletions, a guard-pattern missing across N sites, god-file
|
|
932
|
+
// splits). Only tight clusters — ungrouped TDs stay standalone (user directive). The
|
|
933
|
+
// section is APPENDED to the END of the register, after all individual items.
|
|
934
|
+
// Graph-assisted: an agent may run `gsd-t graph who-imports/who-calls/blast-radius`
|
|
935
|
+
// (project-local bin first, else global) on a TD's files to confirm two TDs truly share
|
|
936
|
+
// code before grouping — grounds the clustering in real relationships, not just prose
|
|
937
|
+
// similarity. Best-effort: any failure leaves the register intact (no section).
|
|
938
|
+
phase("Consolidation");
|
|
939
|
+
try {
|
|
940
|
+
// Number from the SHARED `orderedFindings` so each TD-N here is EXACTLY the id the
|
|
941
|
+
// register wrote (severity → type → original-index — the single ordering source).
|
|
942
|
+
let _n = tdStart;
|
|
943
|
+
const consInput = orderedFindings.map(({ f }) => {
|
|
944
|
+
const td = _n++;
|
|
945
|
+
return `TD-${td} [${f.severity}] (${ascii(f.area) || "?"}) ${ascii(f.title)} @ ${(f.files && f.files[0]) || "?"}`;
|
|
946
|
+
}).join("\n");
|
|
947
|
+
const CONS_SCHEMA = {
|
|
948
|
+
type: "object", required: ["groups"], additionalProperties: false,
|
|
949
|
+
properties: {
|
|
950
|
+
groups: {
|
|
951
|
+
type: "array",
|
|
952
|
+
items: {
|
|
953
|
+
type: "object", required: ["title", "members", "sharedRoot", "recommendedAction"], additionalProperties: false,
|
|
954
|
+
properties: {
|
|
955
|
+
title: { type: "string" },
|
|
956
|
+
members: { type: "array", items: { type: "string" } }, // e.g. ["TD-75","TD-143"]
|
|
957
|
+
sharedRoot: { type: "string" },
|
|
958
|
+
recommendedAction: { type: "string" },
|
|
959
|
+
effort: { type: "string" }, // GSD-T-native units
|
|
960
|
+
},
|
|
961
|
+
},
|
|
962
|
+
},
|
|
963
|
+
notes: { type: "string" },
|
|
964
|
+
},
|
|
965
|
+
};
|
|
966
|
+
const consAgent = await agent(
|
|
967
|
+
[
|
|
968
|
+
`You are identifying CONSOLIDATION OPPORTUNITIES in a tech-debt register — sets of findings that share a ROOT and should be fixed as ONE workstream, not one-by-one.`,
|
|
969
|
+
`Below are ${finalFindings.length} findings as "TD-N [SEVERITY] (area) title @ location".`,
|
|
970
|
+
``,
|
|
971
|
+
`Group ONLY HIGH-CONFIDENCE clusters (user directive — tight, not loose themes):`,
|
|
972
|
+
` • duplicate-functionality families (same logic implemented 2+ times / diverging copies)`,
|
|
973
|
+
` • batch dead-code deletions (multiple never-used modules/components removable together)`,
|
|
974
|
+
` • a single missing pattern repeated across N sites (e.g. the same auth guard absent on many routes)`,
|
|
975
|
+
` • god-file / god-object splits touching several TDs`,
|
|
976
|
+
` • the SAME remediation applied in multiple places`,
|
|
977
|
+
`Findings with no strong shared root MUST be left OUT (they stay standalone — do not force weak groupings).`,
|
|
978
|
+
``,
|
|
979
|
+
`GRAPH-ASSIST (optional, strengthens confidence): to confirm two TDs truly share code, you MAY run the code graph via Bash on their files — try a project-local binary first then the global one:`,
|
|
980
|
+
` \`${projectDir}/bin/gsd-t-graph-query-cli.cjs who-imports <file>\` OR \`gsd-t graph who-imports <file>\` (also who-calls / blast-radius). If the graph is unavailable, fall back to the location/area text — do NOT fail.`,
|
|
981
|
+
``,
|
|
982
|
+
`Return "groups": each = { title, members: ["TD-N",...] (>=2), sharedRoot, recommendedAction (which to keep/delete/extract), effort (GSD-T-native units: domain/wave/spawn count — NEVER human-hours) }. If nothing clusters with high confidence, return groups: [].`,
|
|
983
|
+
``,
|
|
984
|
+
consInput.slice(0, 140000),
|
|
985
|
+
].join("\n"),
|
|
986
|
+
{ label: "consolidation:cluster", phase: "Consolidation", schema: CONS_SCHEMA, model: "opus" }
|
|
987
|
+
).catch((e) => { log(`consolidation cluster failed (non-fatal): ${e && e.message}`); return null; });
|
|
988
|
+
|
|
989
|
+
const groups = (consAgent && Array.isArray(consAgent.groups)) ? consAgent.groups : [];
|
|
990
|
+
if (groups.length) {
|
|
991
|
+
const cs = [
|
|
992
|
+
``, `---`, ``, `## 🧩 Consolidation Opportunities`, ``,
|
|
993
|
+
`> High-confidence clusters of findings that share a root cause and should be addressed as ONE workstream (candidates for a single consolidation milestone). Findings not listed here have no strong shared root and stand alone. Effort is in GSD-T-native units.`, ``,
|
|
994
|
+
];
|
|
995
|
+
groups.forEach((g, gi) => {
|
|
996
|
+
cs.push(`### CG-${gi + 1} - ${ascii(g.title)}`);
|
|
997
|
+
cs.push(`- **Members:** ${(g.members || []).map((m) => ascii(m)).join(", ")}`);
|
|
998
|
+
cs.push(`- **Shared root:** ${ascii(g.sharedRoot)}`);
|
|
999
|
+
cs.push(`- **Recommended action:** ${ascii(g.recommendedAction)}`);
|
|
1000
|
+
if (g.effort) cs.push(`- **Effort:** ${ascii(g.effort)}`);
|
|
1001
|
+
cs.push("");
|
|
1002
|
+
});
|
|
1003
|
+
if (consAgent.notes) cs.push(`> Notes: ${ascii(consAgent.notes)}`, "");
|
|
1004
|
+
// Append via a bounded agent Bash heredoc (orchestrator has no fs).
|
|
1005
|
+
const consBlock = cs.join("\n");
|
|
1006
|
+
await agent(
|
|
1007
|
+
[
|
|
1008
|
+
`APPEND EXACTLY the content between the markers to the END of \`${regPath}\` (append — do NOT overwrite existing content). Use a Bash heredoc: \`cat >> ${regPath} <<'GSDTEOF'\` … \`GSDTEOF\`. After writing, reply ONLY "OK".`,
|
|
1009
|
+
``, `<<<CHUNK>>>`, consBlock, `<<<END_CHUNK>>>`,
|
|
1010
|
+
].join("\n"),
|
|
1011
|
+
{ label: "consolidation:write", phase: "Consolidation", model: "haiku" }
|
|
1012
|
+
).catch((e) => log(`consolidation append uncertain (non-fatal): ${e && e.message}`));
|
|
1013
|
+
log(`consolidation: ${groups.length} high-confidence group(s) appended to register`);
|
|
1014
|
+
} else {
|
|
1015
|
+
log(`consolidation: no high-confidence clusters found (register unchanged)`);
|
|
1016
|
+
}
|
|
1017
|
+
} catch (e) {
|
|
1018
|
+
log(`consolidation phase failed (non-fatal — register intact): ${e && e.message}`);
|
|
1019
|
+
}
|
|
1020
|
+
|
|
892
1021
|
// Document — per-doc fan-out. Each agent writes its file via Write/Edit (its tools).
|
|
893
1022
|
// The orchestrator passes the findings + (for plain-english) tells the agent to
|
|
894
1023
|
// READ the just-written register itself, since the orchestrator can't read it.
|