@tekyzinc/gsd-t 4.0.25 → 4.0.26
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,14 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to GSD-T are documented here. Updated with each release.
|
|
4
4
|
|
|
5
|
+
## [4.0.26] - 2026-06-03 (M78 Plain-English Grouped + Batched - patch)
|
|
6
|
+
|
|
7
|
+
### Fixed - plain-english companion was a flat ungrouped list + would stall on large registers
|
|
8
|
+
|
|
9
|
+
The non-technical companion (techdebt_in_plain_english.md) was generated by a SINGLE doc agent: it produced a flat list with no severity grouping, and would stall writing 300+ entries (the M75 register bug, unfixed for this doc). M78 gives it the register treatment: a dedicated Plain-English phase batches the (severity-sorted) findings, fans out bounded generator agents (shared concurrency gate), then assembles DETERMINISTICALLY with severity section headers (## Critical / High / Medium / Low) and chunk-writes. Removed from docTargets (no longer a single agent). +test/m78-plain-english-grouping.test.js (3 tests: grouped+complete+ordered, no mid-item chunk split, empty-severity omission). Also dropped the stale Render phase from meta.phases (removed back in M71). One-off: regrouped the existing Hilo plain-english doc by authoritative severity.
|
|
10
|
+
|
|
11
|
+
Suite: 1318 pass / 0 fail / 4 skip.
|
|
12
|
+
|
|
5
13
|
## [4.0.25] - 2026-06-03 (M77 HTML Report Reads Deep-Scan Table Format - patch)
|
|
6
14
|
|
|
7
15
|
### Fixed - scan report showed 0 critical/0 high on a 322-finding register
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tekyzinc/gsd-t",
|
|
3
|
-
"version": "4.0.
|
|
3
|
+
"version": "4.0.26",
|
|
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",
|
|
@@ -31,8 +31,8 @@ export const meta = {
|
|
|
31
31
|
{ title: "Probe", detail: "volume probe → per-area slice list", model: "sonnet" },
|
|
32
32
|
{ title: "Deep Scan", detail: "pipeline: per-slice deep finder → single verify" },
|
|
33
33
|
{ title: "Synthesis", detail: "archive prior + write fresh register + git", model: "opus" },
|
|
34
|
-
{ title: "Document",
|
|
35
|
-
{ title: "
|
|
34
|
+
{ title: "Document", detail: "living docs + 5 dimension files (per-doc fan-out)" },
|
|
35
|
+
{ title: "Plain-English", detail: "non-technical companion: batched gen + severity-grouped chunked write" },
|
|
36
36
|
],
|
|
37
37
|
};
|
|
38
38
|
|
|
@@ -645,8 +645,10 @@ const docTargets = [
|
|
|
645
645
|
prompt: `Update or create \`${projectDir}/docs/requirements.md\`: functional requirements from routes/handlers/UI; technical from configs/package.json/runtime; non-functional from perf configs/rate limits/caching.` },
|
|
646
646
|
{ id: "readme", label: "README.md", merge: true,
|
|
647
647
|
prompt: `Update or create \`${projectDir}/README.md\`: project name+description; tech stack+versions; getting-started/setup; brief architecture overview; link to docs/. If it exists, MERGE — preserve the user's structure/custom content.` },
|
|
648
|
-
|
|
649
|
-
|
|
648
|
+
// NOTE (M78): the plain-english companion is NOT in docTargets — a single agent
|
|
649
|
+
// stalls writing 300+ entries (the M75 register bug). It has its own dedicated phase
|
|
650
|
+
// below: batched generation (bounded fan-out) + deterministic severity-grouped,
|
|
651
|
+
// chunked write.
|
|
650
652
|
];
|
|
651
653
|
|
|
652
654
|
const docResults = await parallel(
|
|
@@ -674,6 +676,85 @@ const docsOk = docResults.filter(Boolean).filter((r) => r.status === "written" |
|
|
|
674
676
|
const docsFailed = docResults.filter(Boolean).filter((r) => r.status === "failed");
|
|
675
677
|
log(`document phase: ${docsOk.length}/${docTargets.length} written/merged${docsFailed.length ? `; ${docsFailed.length} failed (non-fatal): ${docsFailed.map((d) => d.doc).join(", ")}` : ""}`);
|
|
676
678
|
|
|
679
|
+
// ─── Plain-English phase (M78) ───────────────────────────────────────────────
|
|
680
|
+
// The non-technical companion can be 300+ entries — a single agent stalls writing
|
|
681
|
+
// it (the M75 register bug). So: batch the (already severity-sorted) findings, fan
|
|
682
|
+
// out bounded generator agents (each writes its batch's entries via the shared gate),
|
|
683
|
+
// then ASSEMBLE deterministically with severity section headers, and chunk-write.
|
|
684
|
+
phase("Plain-English");
|
|
685
|
+
const peTarget = `${projectDir}/.gsd-t/techdebt_in_plain_english.md`;
|
|
686
|
+
const sevLabel = { CRITICAL: "fix before launch", HIGH: "fix soon", MEDIUM: "schedule", LOW: "clean up eventually" };
|
|
687
|
+
// Attach the deterministic TD number (matches the register: severity-sorted, tdStart+).
|
|
688
|
+
const peItems = finalFindings.map((f, i) => ({
|
|
689
|
+
td: tdStart + i, severity: f.severity, title: ascii(f.title),
|
|
690
|
+
area: ascii(f.area), detail: ascii(f.detail || f.impact || "").slice(0, 500),
|
|
691
|
+
}));
|
|
692
|
+
const PE_BATCH = 36;
|
|
693
|
+
const peBatches = [];
|
|
694
|
+
for (let i = 0; i < peItems.length; i += PE_BATCH) peBatches.push(peItems.slice(i, i + PE_BATCH));
|
|
695
|
+
const PE_SCHEMA = { type: "object", required: ["entries"], additionalProperties: false,
|
|
696
|
+
properties: { entries: { type: "array", items: { type: "object", required: ["td", "markdown"], properties: { td: { type: "integer" }, markdown: { type: "string" } } } } } };
|
|
697
|
+
|
|
698
|
+
const peResults = await parallel(peBatches.map((batch, bi) => async () => {
|
|
699
|
+
const prompt = [
|
|
700
|
+
`Write NON-TECHNICAL ("plain English") companion entries for a tech-debt register, for a non-engineer stakeholder (founder/PM).`,
|
|
701
|
+
`For EACH finding below, produce one entry. Return JSON {entries:[{td, markdown}]} where markdown is EXACTLY:`,
|
|
702
|
+
`### TD-<td> - <plain-English name, no jargon>`,
|
|
703
|
+
`**What it is.** <1-2 sentences, no jargon; define any unavoidable term in parentheses>`,
|
|
704
|
+
`**Why it matters.** <business/user consequence>`,
|
|
705
|
+
`**Real-world analogy.** <a concrete everyday comparison that genuinely maps to THIS issue>`,
|
|
706
|
+
`**Severity.** <the plain-urgency phrase given per item>`,
|
|
707
|
+
`Keep the td number EXACTLY. ASCII punctuation only (hyphens, straight quotes — NO em-dashes/smart-quotes/ellipsis). No preamble.`,
|
|
708
|
+
``,
|
|
709
|
+
`Findings (batch ${bi + 1}/${peBatches.length}):`,
|
|
710
|
+
"```json",
|
|
711
|
+
JSON.stringify(batch.map((it) => ({ ...it, severityPhrase: sevLabel[it.severity] || "review" }))),
|
|
712
|
+
"```",
|
|
713
|
+
].join("\n");
|
|
714
|
+
try {
|
|
715
|
+
const r = await gatedAgent(prompt, { label: `plain-english ${bi + 1}/${peBatches.length}`, phase: "Plain-English", schema: PE_SCHEMA, model: "sonnet" });
|
|
716
|
+
return { bi, entries: (r && Array.isArray(r.entries)) ? r.entries : [] };
|
|
717
|
+
} catch (e) { return { bi, entries: [], failed: true }; }
|
|
718
|
+
}));
|
|
719
|
+
// Map td -> entry markdown, then assemble grouped by severity (deterministic headers).
|
|
720
|
+
const peByTd = {};
|
|
721
|
+
for (const r of peResults) for (const e of (r.entries || [])) if (e && e.td != null) peByTd[e.td] = ascii(e.markdown || "");
|
|
722
|
+
const peFailed = peResults.filter((r) => r.failed).length;
|
|
723
|
+
const peGroups = { CRITICAL: [], HIGH: [], MEDIUM: [], LOW: [] };
|
|
724
|
+
for (const it of peItems) { const md = peByTd[it.td]; if (md && peGroups[it.severity]) peGroups[it.severity].push(md.trim()); }
|
|
725
|
+
const peSevHead = { CRITICAL: "## 🔴 Critical", HIGH: "## 🟠 High", MEDIUM: "## 🟡 Medium", LOW: "## 🟢 Low" };
|
|
726
|
+
const peHeader = [
|
|
727
|
+
`# Tech Debt - Plain English`, "",
|
|
728
|
+
`> Non-technical companion to .gsd-t/techdebt.md (Scan${scanNumber ? " #" + scanNumber : ""}, ${peItems.length} findings). One entry per item: what it is, why it matters, a real-world analogy, plain-urgency severity. Grouped by severity.`, "", "---",
|
|
729
|
+
].join("\n");
|
|
730
|
+
// Build chunks: header, then per-severity section (header + its entries), sub-split ≤30KB.
|
|
731
|
+
const peChunks = [peHeader];
|
|
732
|
+
for (const sev of ["CRITICAL", "HIGH", "MEDIUM", "LOW"]) {
|
|
733
|
+
const items = peGroups[sev];
|
|
734
|
+
if (!items.length) continue;
|
|
735
|
+
let buf = `\n${peSevHead[sev]} (${items.length})\n\n`;
|
|
736
|
+
for (const md of items) {
|
|
737
|
+
const piece = md + "\n\n";
|
|
738
|
+
if (buf.length + piece.length > 30000) { peChunks.push(buf); buf = ""; }
|
|
739
|
+
buf += piece;
|
|
740
|
+
}
|
|
741
|
+
if (buf.trim()) peChunks.push(buf);
|
|
742
|
+
}
|
|
743
|
+
let peWrote = 0;
|
|
744
|
+
for (let ci = 0; ci < peChunks.length; ci++) {
|
|
745
|
+
const first = ci === 0;
|
|
746
|
+
const res = await gatedAgent(
|
|
747
|
+
[
|
|
748
|
+
first ? `Create the file \`${peTarget}\` (overwrite) with EXACTLY the content between markers, using the Write tool. Verbatim.`
|
|
749
|
+
: `APPEND EXACTLY the content between markers to the END of \`${peTarget}\` (do not overwrite; append) via a Bash heredoc \`cat >> ${peTarget} <<'GSDTEOF'\` … \`GSDTEOF\`. Verbatim.`,
|
|
750
|
+
`Reply ONLY "OK".`, "", "<<<C>>>", peChunks[ci], "<<<END>>>",
|
|
751
|
+
].join("\n"),
|
|
752
|
+
{ label: `plain-english write ${ci + 1}/${peChunks.length}`, phase: "Plain-English", model: "haiku" }
|
|
753
|
+
).catch((e) => ({ _e: String(e && e.message) }));
|
|
754
|
+
if (typeof res === "string" && /ok/i.test(res)) peWrote++;
|
|
755
|
+
}
|
|
756
|
+
log(`plain-english: ${Object.values(peGroups).reduce((a, b) => a + b.length, 0)}/${peItems.length} entries, grouped by severity, ${peWrote}/${peChunks.length} chunks written${peFailed ? `; ${peFailed} gen batch(es) failed` : ""}`);
|
|
757
|
+
|
|
677
758
|
// Commit the docs + dimension files + plain-english via a small agent (Bash git).
|
|
678
759
|
const commitAgent = await agent(
|
|
679
760
|
[
|