@tekyzinc/gsd-t 2.71.19 → 2.71.21
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 +15 -0
- package/bin/orchestrator.js +56 -10
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to GSD-T are documented here. Updated with each release.
|
|
4
4
|
|
|
5
|
+
## [2.71.21] - 2026-04-08
|
|
6
|
+
|
|
7
|
+
### Fixed (orchestrator — timeout false-pass, review server health, stale cleanup)
|
|
8
|
+
- **Reviewer timeout/kill no longer treated as "pass"** — exit codes 143 (SIGTERM) and 137 (SIGKILL) are now always detected as failures regardless of duration. Previously, a reviewer that timed out at 300s was parsed as "0 issues = pass" because crash detection only checked duration < 10s.
|
|
9
|
+
- **Empty output with non-zero exit also caught** — any reviewer that exits non-zero with no output is treated as a failure, not a clean pass.
|
|
10
|
+
- **Review server health check during human review gate** — every ~30s the polling loop verifies port 3456 is alive. If the review server dies, it auto-restarts. Previously, a dead review server left the orchestrator stuck forever.
|
|
11
|
+
- **Stale auto-review cleanup** — old auto-review files from previous runs are cleared at the start of each phase's review cycle, preventing misleading results from prior orchestrator runs.
|
|
12
|
+
|
|
13
|
+
## [2.71.20] - 2026-04-08
|
|
14
|
+
|
|
15
|
+
### Fixed (orchestrator — reviewer crash false-pass + Ctrl+C + build logging)
|
|
16
|
+
- **Reviewer crash no longer treated as "pass"** — if the reviewer exits with non-zero code in under 10s, it's a crash, not a clean review. Retried on next cycle. Previously, empty output from a crashed reviewer was parsed as "0 issues = pass."
|
|
17
|
+
- **Ctrl+C now works** — replaced `Atomics.wait` with `sleep` command for synchronous polling. `Atomics.wait` blocks the event loop completely, preventing SIGINT.
|
|
18
|
+
- **Build output logging** — builder output written to `.gsd-t/design-review/build-logs/{phase}-build.log` for debugging.
|
|
19
|
+
|
|
5
20
|
## [2.71.18] - 2026-04-08
|
|
6
21
|
|
|
7
22
|
### Fixed (orchestrator — Claude permissions and timeouts)
|
package/bin/orchestrator.js
CHANGED
|
@@ -52,10 +52,12 @@ function ensureDir(dir) {
|
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
function syncSleep(ms) {
|
|
55
|
+
// Use sleep command instead of Atomics.wait — Atomics blocks the event loop
|
|
56
|
+
// completely, preventing SIGINT (Ctrl+C) from being handled
|
|
55
57
|
try {
|
|
56
|
-
|
|
58
|
+
execFileSync("sleep", [String(ms / 1000)], { stdio: "pipe" });
|
|
57
59
|
} catch {
|
|
58
|
-
|
|
60
|
+
// sleep interrupted by signal — that's fine
|
|
59
61
|
}
|
|
60
62
|
}
|
|
61
63
|
|
|
@@ -366,6 +368,7 @@ ${BOLD}Phases:${RESET} ${this.wf.phases.join(" → ")}
|
|
|
366
368
|
openBrowser(`http://localhost:${reviewPort}/review`);
|
|
367
369
|
|
|
368
370
|
// IRONCLAD GATE — JavaScript polling loop
|
|
371
|
+
let healthCheckCounter = 0;
|
|
369
372
|
while (true) {
|
|
370
373
|
if (fs.existsSync(signalPath)) {
|
|
371
374
|
try {
|
|
@@ -374,6 +377,22 @@ ${BOLD}Phases:${RESET} ${this.wf.phases.join(" → ")}
|
|
|
374
377
|
return data;
|
|
375
378
|
} catch { /* malformed — wait for rewrite */ }
|
|
376
379
|
}
|
|
380
|
+
|
|
381
|
+
// Every 10 polls (~30s), verify review server is still alive
|
|
382
|
+
healthCheckCounter++;
|
|
383
|
+
if (healthCheckCounter % 10 === 0) {
|
|
384
|
+
if (!isPortInUse(reviewPort)) {
|
|
385
|
+
warn("Review server is down! Restarting...");
|
|
386
|
+
const devPort = this._activeDevPort || reviewPort - 1283; // fallback heuristic
|
|
387
|
+
const info = this.startReviewServer(projectDir, devPort, reviewPort);
|
|
388
|
+
if (info.pid || info.alreadyRunning) {
|
|
389
|
+
success("Review server restarted");
|
|
390
|
+
} else {
|
|
391
|
+
error("Failed to restart review server — please restart the orchestrator");
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
377
396
|
syncSleep(3000);
|
|
378
397
|
}
|
|
379
398
|
}
|
|
@@ -512,6 +531,7 @@ ${BOLD}Phases:${RESET} ${this.wf.phases.join(" → ")}
|
|
|
512
531
|
const devInfo = this.startDevServer(projectDir, devPort);
|
|
513
532
|
const reviewInfo = this.startReviewServer(projectDir, devPort, reviewPort);
|
|
514
533
|
this.pids = [devInfo.pid, reviewInfo.pid].filter(Boolean);
|
|
534
|
+
this._activeDevPort = devPort;
|
|
515
535
|
}
|
|
516
536
|
|
|
517
537
|
// Register cleanup on exit
|
|
@@ -593,6 +613,16 @@ ${BOLD}Phases:${RESET} ${this.wf.phases.join(" → ")}
|
|
|
593
613
|
// If issues found → spawn fixer Claude → re-measure → re-review until clean.
|
|
594
614
|
const maxAutoReviewCycles = this.wf.defaults?.maxAutoReviewCycles || 4;
|
|
595
615
|
if (this.wf.buildReviewPrompt) {
|
|
616
|
+
// Clear stale auto-review files from previous runs for this phase
|
|
617
|
+
const arDir = path.join(this.getReviewDir(projectDir), "auto-review");
|
|
618
|
+
if (fs.existsSync(arDir)) {
|
|
619
|
+
for (const f of fs.readdirSync(arDir)) {
|
|
620
|
+
if (f.startsWith(`${phase}-`)) {
|
|
621
|
+
try { fs.unlinkSync(path.join(arDir, f)); } catch { /* ignore */ }
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
|
|
596
626
|
let autoReviewCycle = 0;
|
|
597
627
|
let autoReviewClean = false;
|
|
598
628
|
|
|
@@ -607,14 +637,30 @@ ${BOLD}Phases:${RESET} ${this.wf.phases.join(" → ")}
|
|
|
607
637
|
const reviewResult = this.spawnClaude(projectDir, reviewPrompt, reviewTimeout);
|
|
608
638
|
|
|
609
639
|
// Parse reviewer output for issues
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
640
|
+
let issues;
|
|
641
|
+
|
|
642
|
+
// Treat reviewer failure as a failed review, not a pass:
|
|
643
|
+
// - crash: non-zero exit + very short duration (< 10s)
|
|
644
|
+
// - timeout/kill: exit codes 143 (SIGTERM) or 137 (SIGKILL)
|
|
645
|
+
// - empty output with non-zero exit: reviewer produced nothing useful
|
|
646
|
+
const isCrash = reviewResult.exitCode !== 0 && reviewResult.duration < 10;
|
|
647
|
+
const isKilled = [143, 137].includes(reviewResult.exitCode);
|
|
648
|
+
const isEmptyFail = reviewResult.exitCode !== 0 && !reviewResult.output.trim();
|
|
649
|
+
|
|
650
|
+
if (isCrash || isKilled || isEmptyFail) {
|
|
651
|
+
const reason = isCrash ? "crashed" : isKilled ? "killed/timed out" : "failed with no output";
|
|
652
|
+
warn(`Reviewer ${reason} (code ${reviewResult.exitCode}, ${reviewResult.duration}s) — treating as review failure, will retry`);
|
|
653
|
+
issues = [{ component: "ALL", severity: "critical", description: `Reviewer ${reason} with exit code ${reviewResult.exitCode} — review not performed` }];
|
|
616
654
|
} else {
|
|
617
|
-
|
|
655
|
+
issues = this.wf.parseReviewResult
|
|
656
|
+
? this.wf.parseReviewResult(reviewResult.output, phase)
|
|
657
|
+
: this._parseDefaultReviewResult(reviewResult.output);
|
|
658
|
+
|
|
659
|
+
if (reviewResult.exitCode === 0) {
|
|
660
|
+
success(`Reviewer finished in ${reviewResult.duration}s`);
|
|
661
|
+
} else {
|
|
662
|
+
warn(`Reviewer exited with code ${reviewResult.exitCode} after ${reviewResult.duration}s`);
|
|
663
|
+
}
|
|
618
664
|
}
|
|
619
665
|
|
|
620
666
|
// Write review report
|
|
@@ -622,7 +668,7 @@ ${BOLD}Phases:${RESET} ${this.wf.phases.join(" → ")}
|
|
|
622
668
|
ensureDir(reportDir);
|
|
623
669
|
fs.writeFileSync(
|
|
624
670
|
path.join(reportDir, `${phase}-cycle-${autoReviewCycle}.json`),
|
|
625
|
-
JSON.stringify({ cycle: autoReviewCycle, issues, output: reviewResult.output.slice(0, 5000) }, null, 2)
|
|
671
|
+
JSON.stringify({ cycle: autoReviewCycle, issues, exitCode: reviewResult.exitCode, duration: reviewResult.duration, output: reviewResult.output.slice(0, 5000) }, null, 2)
|
|
626
672
|
);
|
|
627
673
|
|
|
628
674
|
if (issues.length === 0) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tekyzinc/gsd-t",
|
|
3
|
-
"version": "2.71.
|
|
3
|
+
"version": "2.71.21",
|
|
4
4
|
"description": "GSD-T: Contract-Driven Development for Claude Code — 56 slash commands with headless CI/CD mode, graph-powered code analysis, real-time agent dashboard, execution intelligence, 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",
|