@tekyzinc/gsd-t 4.17.10 → 4.19.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 CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  All notable changes to GSD-T are documented here. Updated with each release.
4
4
 
5
+ ## [4.19.10] - 2026-07-07
6
+
7
+ ### Added — `/gsd-t-stories`: dev-team handoff user stories in the Tekyz format
8
+
9
+ New standalone command that generates a development-team handoff document in the Tekyz user-stories format from any source — a scan register, a requirements doc, a design contract, or a reverse-engineered codebase. Reverse-engineered from the Tekyz Compass sample PRD: front matter → Application Flow Overview → User Stories grouped under Epics (id, story, workflow, grouped acceptance criteria, per-story flow diagram, mapped test-case table) → Scope Summary. Diagrams are authored as Mermaid but embedded as rendered PNGs (matching the sample's embedded-image flow charts), via `@mermaid-js/mermaid-cli`; output to `share/<Repo>-user-stories.md` (+ optional `.docx` via pandoc).
10
+
11
+ - `commands/gsd-t-stories.md`: the new command (input classification, epic/story decomposition, Mermaid→PNG→embed pipeline, markdown + optional docx output).
12
+ - `templates/playbooks/tekyz-user-stories-format.md`: bundled format reference (the exact structure + wording conventions).
13
+ - `commands/gsd-t-help.md`, `README.md`, `templates/CLAUDE-global.md`: command reference + standalone-list ripple.
14
+ - `test/filesystem.test.js`: command counts 52→53, gsdt 46→47.
15
+
16
+ Distinct from `/gsd-t-prd` (which writes the internal `docs/prd.md`) — this is an external client/dev deliverable. Suite 2631/2631.
17
+
18
+ ## [4.18.10] - 2026-07-03
19
+
20
+ ### Added — Scan Consolidation Opportunities + type-grouped register; init-scan-setup repo adoption
21
+
22
+ 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.
23
+
24
+ - `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.
25
+ - `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/`).
26
+ - `commands/gsd-t-scan.md`: synthesis/consolidation phase description.
27
+ - `.gsd-t/backlog.md`: split logging item #48 → #48 (trace) + #49 (audit) with full specs.
28
+ - Lints green: M71 runtime-native, M85 tier-policy, M76 ascii. Suite 2631/2631.
29
+
5
30
  ## [4.17.10] - 2026-07-01
6
31
 
7
32
  ### 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.17.10** - A methodology for reliable, parallelizable development using Claude Code with optional Agent Teams support.
3
+ **v4.19.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.
@@ -187,6 +187,7 @@ This will replace changed command files, back up your CLAUDE.md if customized, a
187
187
  | `/gsd-t-gap-analysis` | Requirements gap analysis — spec vs. existing code | Manual |
188
188
  | `/gsd-t-promote-debt` | Convert techdebt items to milestones | Manual |
189
189
  | `/gsd-t-estimate` | Turn any work doc (scan, requirements, feature/app spec) into a Tekyz client estimate (Google Sheet: T-Shirt Size + Team Mix) + matching PRD — supervised, with an operator-arbitrated Estimate Red Team | Manual |
190
+ | `/gsd-t-stories` | Generate a dev-team handoff doc in the Tekyz user-stories format (stories + workflows + acceptance criteria + Mermaid flow diagrams + mapped test cases) from any source | Manual |
190
191
  | `/gsd-t-populate` | Auto-populate docs from existing codebase | Manual |
191
192
  | `/gsd-t-design-decompose` | Decompose design into element/widget/page contracts | Manual |
192
193
 
@@ -364,6 +364,12 @@ Use these when user asks for help on a specific command:
364
364
  - **Updates**: the Tekyz estimate Google Sheet + `share/<Repo>-PRD-*.md` (and, if renumbered, the source doc/docs/scan files) + optional `share/<Repo>-estimate-redteam-notes.md`
365
365
  - **Use when**: You need a client-facing paid estimate + PRD (T-shirt sizing, dollar range, sign-off) from a scan OR a requirements/feature/app spec. **SUPERVISED** — judgment phases (sizing, adjustments, PRD, Red Team) pause for your review; **you are the final arbiter** of an Estimate Red Team that challenges the numbers. Rate + sheet template + factors are parameterized (default Tekyz). Encodes the Tekyz playbook (`~/.claude/playbooks/tekyz-estimation-and-prd-playbook.md`)
366
366
 
367
+ ### stories
368
+ - **Summary**: Generate a dev-team handoff document in the Tekyz user-stories format — discrete user stories with workflows, grouped acceptance criteria, per-story flow diagrams (Mermaid rendered to embedded images), and mapped test-case tables — from any source (scan register, requirements doc, design contract, or a reverse-engineered codebase)
369
+ - **Auto-invoked**: No
370
+ - **Updates**: new deliverable `share/<Repo>-user-stories.md` (+ optional `.docx` via pandoc) + `share/media/*.png` (rendered diagrams) + `.gsd-t/user-stories/diagrams/*.mmd`
371
+ - **Use when**: You need to hand a development team discrete, testable user stories in the Tekyz handoff style. Distinct from `/gsd-t-prd` (which writes the INTERNAL `docs/prd.md`) — this is an EXTERNAL client/dev deliverable. Diagrams are authored as Mermaid but embedded as rendered images (needs `@mermaid-js/mermaid-cli`). Format reference: `~/.claude/playbooks/tekyz-user-stories-format.md`
372
+
367
373
  ### populate
368
374
  - **Summary**: Auto-populate all living docs from existing codebase analysis
369
375
  - **Auto-invoked**: No
@@ -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
- **If `.gsd-t/` already exists**: Skip init it's already done. Log and continue to scan. Still check and create settings.local (step 5) even if init is skipped.
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, graph indexing (`.gsd-t/graph/`) will run as part of the scan phase (Step 4). The graph provides code-level queries (callers, callees, domain ownership, dead code detection) that enhance all subsequent GSD-T commands. No separate setup is needed — the scan handles it.
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
- Execute the full scan by invoking the volume-scaled scan Workflow (same as `/gsd-t-scan` `templates/workflows/gsd-t-scan.workflow.js`):
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. The volume-probe slices the codebase by area (scaling with volume, not a fixed dimension set); per-slice deep finders enumerate; a single verify pass drops false positives.
107
- 2. Synthesis builds the `.gsd-t/techdebt.md` register and the `.gsd-t/scan/*.md` analysis files.
108
- 3. Cross-populate findings into living documents (docs/architecture.md, docs/workflows.md, docs/infrastructure.md, docs/requirements.md) from the register + analysis files.
109
- 4. Update README.md with discovered tech stack and setup info.
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
- **If `.gsd-t/techdebt.md` already exists**: the Workflow archives it to `.gsd-t/techdebt_YYYY-MM-DD.md` and continues TD numbering.
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
 
@@ -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
 
@@ -0,0 +1,68 @@
1
+ # GSD-T: Stories — Dev-Handoff User Stories (Tekyz format) from any source
2
+
3
+ You are generating a **development-team handoff document** in the Tekyz user-stories format: discrete user stories with workflows, grouped acceptance criteria, per-story flow diagrams (Mermaid rendered to embedded images), and mapped test cases. Input can be a GSD-T **scan register**, a **requirements doc**, a **design contract**, or an **existing codebase** (reverse-engineered). `$ARGUMENTS` may carry `--input <path>`, `--source <scan|requirements|design|code>`, `--prefix <PREFIX>`, `--phase-filter <MVP|...>`, `--docx`.
4
+
5
+ **Canonical format reference (READ FIRST):** `~/.claude/playbooks/tekyz-user-stories-format.md` if present, else the bundled `templates/playbooks/tekyz-user-stories-format.md` in the GSD-T package. It encodes the exact structure + wording conventions reverse-engineered from the Tekyz sample (`Compass_Sample_PRD_2.docx`). This command FILLS that format from the real input.
6
+
7
+ > **Client/dev-team deliverable — distinct from `/gsd-t-prd`.** `/gsd-t-prd` writes the INTERNAL `docs/prd.md` that feeds the GSD-T pipeline. THIS command emits an EXTERNAL, dev-ready handoff doc in the Tekyz user-story style, saved to `share/`.
8
+
9
+ ## Step 0: Inputs + Source Classification
10
+
11
+ 1. **Resolve the input** (from `$ARGUMENTS --input`, else auto-detect in priority order): `.gsd-t/techdebt.md` (scan) → `docs/requirements.md` → `.gsd-t/contracts/design-contract.md` / `.gsd-t/contracts/design/` → the codebase itself. If none usable → ask the user what to document.
12
+ 2. **Classify the source** so the extraction method matches:
13
+ - **scan register** → stories derive from the functional gaps/features implied by findings (each cluster of related TDs → a story; Reqs cross-ref the TD-N ids).
14
+ - **requirements doc** → each requirement / requirement-group → a story (Reqs cross-ref R-N/FR-N).
15
+ - **design contract** → each screen/widget/flow → a story.
16
+ - **codebase (reverse-engineer)** → walk real features (routes, pages, flows) and reverse-engineer the discrete user stories + test cases from what's built. **Use the code graph** (`gsd-t graph cluster` / `who-imports` / `who-calls`) to find feature clusters and real interaction paths — do NOT grep-guess the architecture.
17
+ 3. **Derive the story-id `PREFIX`** from the product name (Compass→`CMPS`, NiceNote→`NN`, Newman Avatar→`NAV`) unless `--prefix` is given. Confirm with the user.
18
+ 4. **Confirm scope** — which phases/epics to include — before generating.
19
+
20
+ ## Step 1: Application Flow Overview (§1)
21
+
22
+ Produce the numbered end-to-end user journey (1..N major steps, plain language). Then author the **app-flow chart** as a single Mermaid flowchart of the whole journey and render+embed it (Step 4). This is the reader's map before the story detail.
23
+
24
+ ## Step 2: Decompose into Epics + Stories (§2)
25
+
26
+ Group functionality into **Epics** (`EP-NN: <name>`), each with a `Phase:`. Under each epic, emit discrete stories. **A story is ONE coherent capability a user exercises** — not a whole feature area, not a single UI control. Right granularity = the sample's (e.g. "Avatar renders full-screen with state indicators" is one story; "Session controls: pause/resume/end" is another).
27
+
28
+ For EACH story, author in EXACTLY this order (per the format reference):
29
+
30
+ 1. **`<PREFIX>-NNN — <Story Title>`** (sequential, zero-padded).
31
+ 2. **Meta line** — `*Phase: … | Reqs: <source ids> | Test Cases: <N>*`. `Reqs:` cross-references the source (TD-N / R-N / FR-N); `Test Cases:` = row count of this story's table.
32
+ 3. **Story:** `*As a <role>, I want <capability>, so that <benefit>.*` (strict template, one sentence.)
33
+ 4. **Workflow:** numbered concrete interaction steps (user action → system response), present tense.
34
+ 5. **Acceptance Criteria:** bulleted, **grouped by sub-area** with a bold sub-heading per group; include error/edge/recovery groups. Each bullet one testable assertion.
35
+ 6. **Flow Diagram:** a per-story Mermaid flowchart of THIS story's workflow → rendered PNG → embedded (Step 4).
36
+ 7. **Mapped Test Cases:** table `TC ID | Type | Test Title | Expected Result`. `TC-NNN` sequential across the whole doc; `Type` ∈ {Positive, Negative, Edge}; cover happy-path + failure + boundary; every acceptance-criteria group represented by ≥1 test case.
37
+
38
+ **No confabulation** (`feedback_no_confabulated_examples`): every story, workflow step, and test case must trace to something in the actual input source. If the source is silent on a needed detail, mark it a `[TODO: confirm with client]` — never invent product specifics.
39
+
40
+ ## Step 3: Scope Summary (§3)
41
+
42
+ A table mapping every story → phase → epic: `| Story ID | Title | Phase | Epic |`. Gives the team the MVP-vs-later-phase picture at a glance.
43
+
44
+ ## Step 4: Diagrams — Mermaid rendered to EMBEDDED images (MANDATORY method)
45
+
46
+ Per the standing directive, diagrams are authored as Mermaid but **embedded as rendered images** (matching the sample's `<img>` flow charts) — NOT left as raw Mermaid text.
47
+
48
+ 1. Write each diagram's Mermaid source to `.gsd-t/user-stories/diagrams/<name>.mmd` (app-flow = `app-flow`, per-story = `<PREFIX>-NNN-flow`).
49
+ 2. Render each to PNG beside the deliverable: `mmdc -i <src>.mmd -o share/media/<name>.png` (Mermaid CLI `@mermaid-js/mermaid-cli`; fall back to `npx @mermaid-js/mermaid-cli -i … -o …`).
50
+ 3. **If `mmdc` is unavailable → HALT** and tell the user to install it (`npm i -g @mermaid-js/mermaid-cli`). Do NOT silently ship raw Mermaid where an embedded image is expected (`feedback_no_silent_degradation`).
51
+ 4. Embed each rendered PNG in the markdown at its `Flow Diagram:` / §1 position: `![Flow Diagram](media/<name>.png)`.
52
+ 5. Keep the `.mmd` source (editable, version-controllable) alongside the PNG.
53
+
54
+ ## Step 5: Assemble + Deliver
55
+
56
+ 1. Assemble front matter (product, "Prepared by Tekyz Inc.", version, source-of-truth note; SAMPLE disclaimer only if illustrative) + §1 + §2 + §3.
57
+ 2. **Write the markdown** to `share/<Repo>-user-stories.md`, with rendered diagram PNGs in `share/media/`.
58
+ 3. **If `--docx`** (or the user wants Word): convert with `pandoc share/<Repo>-user-stories.md -o share/<Repo>-user-stories.docx` — pandoc embeds the PNGs as real Word images, matching the handoff sample. Report if pandoc is missing (don't fail the markdown).
59
+ 4. **Report:** story count, epic count, total test cases, diagram count, and the output paths.
60
+
61
+ ## Document Ripple
62
+
63
+ - New deliverable(s): `share/<Repo>-user-stories.md` (+ `.docx` if requested) + `share/media/*.png` + `.gsd-t/user-stories/diagrams/*.mmd`.
64
+ - No living-doc changes (this is an external handoff artifact) — but log a `.gsd-t/progress.md` Decision Log entry noting the deliverable + source.
65
+
66
+ ## ▶ Next Up
67
+
68
+ Standalone command — no auto-successor. After generating, hand the `share/<Repo>-user-stories.md` (or `.docx`) to the development team.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tekyzinc/gsd-t",
3
- "version": "4.17.10",
3
+ "version": "4.19.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",
@@ -460,7 +460,7 @@ Add `**Also available:**` with `- /gsd-t-{alt} — {desc}` lines if alternatives
460
460
  | `setup` | `status` | |
461
461
  | `design-decompose` | `design-build` | `partition` (if domains needed first) |
462
462
 
463
- Commands with no successor (standalone): `quick`, `debug`, `brainstorm`, `status`, `help`, `resume`, `prompt`, `log`, `health`, `pause`, `estimate`, backlog commands.
463
+ Commands with no successor (standalone): `quick`, `debug`, `brainstorm`, `status`, `help`, `resume`, `prompt`, `log`, `health`, `pause`, `estimate`, `stories`, backlog commands.
464
464
 
465
465
  Skip the hint if auto-advancing (Level 3 mid-wave) — only show when the user needs to manually invoke the next step.
466
466
 
@@ -0,0 +1,127 @@
1
+ # Tekyz User-Stories / Dev-Handoff PRD Format
2
+
3
+ > The canonical format for a Tekyz **development-team handoff** document: discrete user
4
+ > stories with workflows, grouped acceptance criteria, per-story flow diagrams, and mapped
5
+ > test cases. Reverse-engineered from `Compass_Sample_PRD_2.docx` (the sample Tekyz uses to
6
+ > illustrate its requirements-documentation process). `/gsd-t-user-stories` emits THIS format.
7
+ >
8
+ > This is a REFERENCE for structure + exact wording conventions — not the content. The
9
+ > command fills it from the actual input source (scan / requirements / design / codebase).
10
+
11
+ ---
12
+
13
+ ## Document skeleton (in order)
14
+
15
+ ```
16
+ FRONT MATTER
17
+ 1. Application Flow Overview (numbered end-to-end journey + app-flow chart)
18
+ 2. User Stories — Detailed Requirements
19
+ EP-NN: <Epic Name> (epic grouping)
20
+ Phase: <MVP | Phase 1 | ...>
21
+ <PREFIX>-NNN — <Story Title>
22
+ meta line
23
+ Story: (As a … I want … so that …)
24
+ Workflow: (numbered steps)
25
+ Acceptance Criteria: (grouped by sub-area, bulleted)
26
+ Flow Diagram: (rendered Mermaid PNG, embedded as <img>)
27
+ Mapped Test Cases: (table)
28
+ 3. <Phase> vs <Phase> Scope Summary (story→phase→epic table)
29
+ ```
30
+
31
+ ---
32
+
33
+ ## FRONT MATTER (verbatim conventions)
34
+
35
+ ```
36
+ **<PRODUCT NAME>**
37
+
38
+ <one-line product descriptor>
39
+
40
+ Product Requirements Document
41
+
42
+ **Prepared by Tekyz Inc.**
43
+
44
+ Version <X.Y> — <source-of-truth note, e.g. "Design file v2.x as source of truth">
45
+ ```
46
+
47
+ If the doc is illustrative/sample, include the italic SAMPLE DOCUMENT disclaimer block.
48
+
49
+ ---
50
+
51
+ ## §1 — Application Flow Overview
52
+
53
+ A numbered list (1..N) naming each major step of the end-to-end user journey in plain
54
+ language (e.g. "First-Time User Launch: App opens directly into … onboarding"). Follow with:
55
+ - an **app-flow chart** — a single Mermaid flowchart of the whole journey, rendered to PNG
56
+ and embedded (`![Application Flow](media/app-flow.png)`), matching the sample's companion chart.
57
+ - the italic note that a complete flow chart accompanies the document.
58
+
59
+ ---
60
+
61
+ ## §2 — User Stories (the core; repeat per story)
62
+
63
+ Group stories under **Epics**: `**EP-01: <Epic Name>**` then `**Phase: <phase>**`.
64
+
65
+ Each story, in EXACTLY this order:
66
+
67
+ **Story ID + title:** `**<PREFIX>-NNN — <Story Title>**`
68
+ - `<PREFIX>` is a per-project story-id prefix (Compass used `CMPS`). Derive from the product
69
+ name (e.g. NiceNote → `NN`, Newman Avatar → `NAV`). Sequential, zero-padded to 3.
70
+
71
+ **Meta line** (italic, pipe-separated):
72
+ `*Phase: <phase> | Reqs: <R-… list> | Test Cases: <N>*`
73
+ - `Reqs:` cross-references source requirement ids (from a scan register's TD-N, a requirements
74
+ doc's R-N/FR-N, or synthesized ids). `Test Cases:` = count of rows in the Mapped Test Cases table.
75
+
76
+ **Story:** one sentence, italic, strict template:
77
+ `*As a <role>, I want <capability>, so that <benefit>.*`
78
+
79
+ **Workflow:** numbered steps (1..N) describing the concrete interaction sequence — what the
80
+ user does and what the system does in response. Present tense, specific.
81
+
82
+ **Acceptance Criteria:** bulleted, **grouped by sub-area** with a bold sub-heading per group
83
+ (e.g. `**Talking State:**`, `**Error and Recovery:**`). Each bullet is a single testable
84
+ assertion (state changed / content shown / constraint met). Include error/edge/recovery groups.
85
+
86
+ **Flow Diagram:** a per-story Mermaid flowchart of THIS story's workflow, rendered to PNG and
87
+ embedded (`![Flow Diagram](media/<PREFIX>-NNN-flow.png)`) — matching the sample's per-story `<img>`.
88
+
89
+ **Mapped Test Cases:** a markdown table with columns **TC ID | Type | Test Title | Expected Result**.
90
+ - `TC ID`: `TC-NNN` sequential across the whole document.
91
+ - `Type`: one of **Positive | Negative | Edge**.
92
+ - Cover the happy path (Positive), failure modes (Negative), and boundaries (Edge). Every
93
+ acceptance-criteria group should be represented by at least one test case.
94
+
95
+ ---
96
+
97
+ ## §3 — Scope Summary
98
+
99
+ A table mapping every story to its phase + epic, so a team sees MVP vs later-phase scope at a glance:
100
+
101
+ `| Story ID | Title | Phase | Epic |`
102
+
103
+ ---
104
+
105
+ ## Diagram pipeline (Mermaid → embedded image)
106
+
107
+ Per the user directive, diagrams are **authored as Mermaid but EMBEDDED as rendered images**
108
+ (like the sample's `<img>` flow charts) — not left as raw Mermaid text.
109
+
110
+ 1. Write each diagram's Mermaid source to `.gsd-t/user-stories/diagrams/<name>.mmd`.
111
+ 2. Render to PNG with the Mermaid CLI: `mmdc -i <name>.mmd -o media/<name>.png` (installed:
112
+ `@mermaid-js/mermaid-cli`; fall back to `npx @mermaid-js/mermaid-cli`). If `mmdc` is
113
+ unavailable, HALT and tell the user to install it — do NOT silently ship raw Mermaid where
114
+ an embedded image is expected (no-silent-degradation).
115
+ 3. Embed the PNG in the markdown: `![Flow Diagram](media/<name>.png)`.
116
+ 4. On `.docx` conversion, pandoc embeds these PNGs as real Word images — matching the sample.
117
+
118
+ Keep the `.mmd` source (version-controllable, editable) alongside the rendered PNG.
119
+
120
+ ---
121
+
122
+ ## Output
123
+
124
+ - Markdown deliverable → `share/<Repo>-user-stories.md` (GSD-T share convention), with a
125
+ `media/` dir of rendered diagram PNGs beside it.
126
+ - Optional `.docx` (matching the handoff sample) via `pandoc share/<Repo>-user-stories.md
127
+ -o share/<Repo>-user-stories.docx` — embeds the PNGs as Word images.
@@ -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
- for (const f of finalFindings) {
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.