@tekyzinc/gsd-t 4.0.22 → 4.0.23
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/package.json +1 -1
- package/templates/workflows/gsd-t-scan.workflow.js +29 -15
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.0.23] - 2026-06-03 (M76 ASCII-Clean Register Output — patch)
|
|
6
|
+
|
|
7
|
+
### Fixed — register/docs rendered as mojibake in non-UTF-8 terminals
|
|
8
|
+
|
|
9
|
+
The scan register used emoji severity markers (🔴🟠🟡🟢) and em-dashes. In a non-UTF-8 terminal/pager these display as garbage boxes (`βPADCCH`/`πAPCCCH`). The file bytes were valid UTF-8 — purely a render problem — but the emoji added nothing and made the register unreadable in common viewers.
|
|
10
|
+
|
|
11
|
+
- `templates/workflows/gsd-t-scan.workflow.js`: added `ascii()` sanitizer (strips emoji/symbols, normalizes em/en dashes → `-`, smart quotes → ASCII, ellipsis → `...`). `fmtChunks` now emits plain-ASCII headers/tables and sanitizes every user-supplied field (finder text can contain these too). Document-phase agents instructed "ASCII ONLY".
|
|
12
|
+
- `test/m76-ascii-clean-register.test.js`: +5 tests (sanitizer behavior + a structural guard that fmtChunks' output literals contain no emoji/em-dash).
|
|
13
|
+
- One-off: cleaned the existing HiloAviation scan docs (techdebt.md + plain-english + 5 dimension files) — 0 emoji / 0 em-dashes remaining.
|
|
14
|
+
|
|
15
|
+
Suite: 1311 pass / 0 fail / 4 skip — zero regressions.
|
|
16
|
+
|
|
5
17
|
## [4.0.22] - 2026-06-02 (M75 Deterministic Chunked Register Write — patch)
|
|
6
18
|
|
|
7
19
|
### Fixed — synthesis no longer stalls writing a large 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.23",
|
|
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",
|
|
@@ -494,30 +494,43 @@ counts.total = finalFindings.length;
|
|
|
494
494
|
// of a 466KB register truncates at ~165KB — verified). Chunk 0 is the header+summary
|
|
495
495
|
// (Write/create); the rest are appends. Items are grouped so a chunk never splits an
|
|
496
496
|
// item, and the severity-section heading rides with its first item's chunk.
|
|
497
|
+
// M76: register output must be ASCII-clean — no emoji, no em/en-dashes, no smart
|
|
498
|
+
// quotes. They render as mojibake boxes in non-UTF-8 terminals/pagers (the
|
|
499
|
+
// βPADCCH/πAPCCCH garbage a user saw). Finder text can also contain these, so sanitize
|
|
500
|
+
// every user-supplied field, not just our own template strings.
|
|
501
|
+
function ascii(s) {
|
|
502
|
+
return String(s == null ? "" : s)
|
|
503
|
+
.replace(/[\u{1F300}-\u{1FAFF}\u{2600}-\u{27BF}\u{2190}-\u{21FF}\u{2B00}-\u{2BFF}️]/gu, "") // emoji/symbols
|
|
504
|
+
.replace(/[—–]/g, "-") // em/en dash -> hyphen
|
|
505
|
+
.replace(/[‘’]/g, "'") // smart single quotes
|
|
506
|
+
.replace(/[“”]/g, '"') // smart double quotes
|
|
507
|
+
.replace(/…/g, "...") // ellipsis
|
|
508
|
+
.replace(/[ \t]+\n/g, "\n"); // tidy trailing whitespace
|
|
509
|
+
}
|
|
497
510
|
function fmtChunks(today) {
|
|
498
|
-
const sevHead = { CRITICAL: "
|
|
511
|
+
const sevHead = { CRITICAL: "Critical", HIGH: "High", MEDIUM: "Medium", LOW: "Low" };
|
|
499
512
|
const head = [];
|
|
500
|
-
head.push(`# Tech Debt Register
|
|
501
|
-
if (scanNumber) head.push(`**Scan #${scanNumber}**
|
|
513
|
+
head.push(`# Tech Debt Register - ${projectDir.split("/").pop()}`, "");
|
|
514
|
+
if (scanNumber) head.push(`**Scan #${scanNumber}** - Deep codebase scan (runtime-native, ${coverageComplete ? "full coverage" : "PARTIAL coverage"})`);
|
|
502
515
|
head.push(`**Date:** ${today}`);
|
|
503
|
-
head.push(`**Slices run:** ${slices.length} | **Coverage:** ${coverageComplete ? `FULL
|
|
516
|
+
head.push(`**Slices run:** ${slices.length} | **Coverage:** ${coverageComplete ? `FULL - all ${slices.length} slices succeeded` : `PARTIAL - ${succeededCount}/${slices.length} succeeded`}`);
|
|
504
517
|
head.push(`**Verified findings:** ${counts.total}`, "");
|
|
505
518
|
head.push(`> Effort estimates use GSD-T-native units (domain / wave / spawn / token-spend). Never human-hours.`);
|
|
506
519
|
head.push(`> TD numbering continues from the prior register (if any, archived). This scan begins at **TD-${tdStart}**.`, "");
|
|
507
|
-
if (!coverageComplete) head.push(`>
|
|
520
|
+
if (!coverageComplete) head.push(`> [!] **PARTIAL COVERAGE - ${failedSlices.length} of ${slices.length} codebase areas were NOT scanned this pass** (failed to return findings): ${ascii(failedSlices.join(", "))}. Findings UNDER-COUNT the real debt. Re-run (resume) for full coverage.`, "");
|
|
508
521
|
head.push(`## Summary`, "", `| Severity | Count |`, `|----------|-------|`,
|
|
509
|
-
`|
|
|
510
|
-
`|
|
|
522
|
+
`| CRITICAL | ${counts.critical} |`, `| HIGH | ${counts.high} |`,
|
|
523
|
+
`| MEDIUM | ${counts.medium} |`, `| LOW | ${counts.low} |`,
|
|
511
524
|
`| **Total** | **${counts.total}** |`, "", "---", "");
|
|
512
525
|
|
|
513
526
|
function itemMd(f, td) {
|
|
514
|
-
const L = [`### TD-${td}
|
|
515
|
-
`- **Area:** ${f.area || "
|
|
516
|
-
`- **Location:** ${(f.files && f.files.length) ? f.files.join(", ") : "
|
|
517
|
-
if (f.detail) L.push(`- **Description:** ${f.detail}`);
|
|
518
|
-
if (f.impact) L.push(`- **Impact:** ${f.impact}`);
|
|
519
|
-
if (f.recommendation) L.push(`- **Remediation:** ${f.recommendation}`);
|
|
520
|
-
if (f.slice) L.push(`- **Found in slice:** ${f.slice}`);
|
|
527
|
+
const L = [`### TD-${td} - ${ascii(f.title) || "(untitled)"}`,
|
|
528
|
+
`- **Area:** ${ascii(f.area) || "(none)"}`, `- **Severity:** ${f.severity}`, `- **Status:** OPEN`,
|
|
529
|
+
`- **Location:** ${(f.files && f.files.length) ? ascii(f.files.join(", ")) : "(none)"}`];
|
|
530
|
+
if (f.detail) L.push(`- **Description:** ${ascii(f.detail)}`);
|
|
531
|
+
if (f.impact) L.push(`- **Impact:** ${ascii(f.impact)}`);
|
|
532
|
+
if (f.recommendation) L.push(`- **Remediation:** ${ascii(f.recommendation)}`);
|
|
533
|
+
if (f.slice) L.push(`- **Found in slice:** ${ascii(f.slice)}`);
|
|
521
534
|
L.push("");
|
|
522
535
|
return L.join("\n");
|
|
523
536
|
}
|
|
@@ -646,7 +659,8 @@ const docResults = await parallel(
|
|
|
646
659
|
d.prompt,
|
|
647
660
|
``,
|
|
648
661
|
isLiving ? mergeNote : `Write the file fresh in the format described (use Bash \`mkdir -p\` for parent dirs if needed).`,
|
|
649
|
-
`
|
|
662
|
+
`ASCII ONLY: do NOT use emoji, em-dashes (use " - "), en-dashes, smart quotes, or other non-ASCII punctuation — they render as garbage in non-UTF-8 terminals. Plain ASCII markdown only.`,
|
|
663
|
+
`Read the actual code under the relevant slice paths for specifics - don't summarize only from findings. Use Write/Edit to write the file, then return JSON per the schema (status "written"/"merged"/"skipped"/"failed"). Do NOT commit - the workflow handles git at the end.`,
|
|
650
664
|
].filter(Boolean).join("\n");
|
|
651
665
|
try {
|
|
652
666
|
return await agent(prompt, { label: d.label, phase: "Document", schema: DOC_RESULT_SCHEMA, model: "sonnet" });
|