@tekyzinc/gsd-t 4.9.12 → 4.9.14
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 +19 -0
- package/README.md +1 -1
- package/bin/gsd-t.js +24 -33
- package/package.json +1 -1
- package/scripts/gsd-t-auto-route.js +21 -0
- package/templates/CLAUDE-global.md +1 -1
- package/templates/workflows/gsd-t-phase.workflow.js +15 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,25 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to GSD-T are documented here. Updated with each release.
|
|
4
4
|
|
|
5
|
+
## [4.9.14] - 2026-06-25 (workflow runCli retry — reliability patch)
|
|
6
|
+
|
|
7
|
+
### Fixed — transient helper-call flakes no longer false-block workflows
|
|
8
|
+
|
|
9
|
+
`runCli` (the inline helper every `*.workflow.js` uses to run a GSD-T CLI command) delegates the call to a haiku helper agent, not a subprocess — so a return can transiently come back missing its parsed result, surfacing later as a cryptic `classify failed: no envelope` or a fail-closed gate block even though the CLI itself succeeded. Caught live during the M94 plan phase: the traceability gate passed when run directly (exit 0) but the workflow's confirm-call came back unparsed and failed closed.
|
|
10
|
+
|
|
11
|
+
- `runCli` now **retries once** when JSON was expected but no parsed result returned (covers both the throw path and a malformed return the loose schema let through). A real CLI failure that returned valid JSON (`ok:false` with a present envelope) is **not** retried — a true result, not a transient miss.
|
|
12
|
+
- Hardens every phase/wave/integrate/debug workflow's CLI calls (preflight, verify-gate, traceability, brief, build-coverage, test-data).
|
|
13
|
+
|
|
14
|
+
## [4.9.13] - 2026-06-25 (M93 — Reader Contract every turn, retire the selective gate — patch)
|
|
15
|
+
|
|
16
|
+
### Changed — concise output is now enforced on EVERY reply, not by pattern-matching
|
|
17
|
+
|
|
18
|
+
The deterministic brevity-guard Stop hook kept missing verbose replies: a gate that matches a hardcoded list of known-bad opener phrases can only ever be **selective** — every new phrasing slips through (whack-a-mole). Per the user, the fix is to stop detecting and always apply the standard.
|
|
19
|
+
|
|
20
|
+
- **Retired the brevity-guard Stop hook.** `gsd-t update-all` now *removes* it from any install that still has it.
|
|
21
|
+
- **Reader Contract injected every turn** by the `UserPromptSubmit` hook (`scripts/gsd-t-auto-route.js`) — applies to ALL reader-facing output in every project: assume the first draft is wordy and rewrite it tight; answer first (intent-first only when about to change code); gloss jargon; bullets over paragraphs. Includes generic before→after examples that teach the shape.
|
|
22
|
+
- This is wording-independent (no enumeration of bad phrasings) and universal (the standard is in front of the model each turn, not a backstop guessing which replies are bad).
|
|
23
|
+
|
|
5
24
|
## [4.9.12] - 2026-06-23 (M93 brevity-guard live-tuning — patch)
|
|
6
25
|
|
|
7
26
|
### Changed — brevity guard catches more, false-blocks less
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# GSD-T: Contract-Driven Development for Claude Code
|
|
2
2
|
|
|
3
|
-
**v4.9.
|
|
3
|
+
**v4.9.14** - 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.
|
package/bin/gsd-t.js
CHANGED
|
@@ -395,48 +395,39 @@ function addHeartbeatHook(hooks, event, cmd) {
|
|
|
395
395
|
return true;
|
|
396
396
|
}
|
|
397
397
|
|
|
398
|
-
// ───
|
|
399
|
-
//
|
|
400
|
-
//
|
|
401
|
-
//
|
|
398
|
+
// ─── Reader Contract (M93) ──────────────────────────────────────────────────
|
|
399
|
+
// RETIRED the deterministic brevity-guard Stop hook: a pattern-matching gate can
|
|
400
|
+
// only ever be SELECTIVE (it catches the phrasings it enumerates and misses every
|
|
401
|
+
// new one — the whack-a-mole the user hit). Replaced by the every-turn Reader
|
|
402
|
+
// Contract injected by scripts/gsd-t-auto-route.js (the UserPromptSubmit hook):
|
|
403
|
+
// the standard is put in front of the model each turn, applied to ALL replies,
|
|
404
|
+
// instead of a backstop guessing which ones are bad. This function now REMOVES
|
|
405
|
+
// the old Stop hook from any install that still has it.
|
|
402
406
|
|
|
403
407
|
const BREVITY_GUARD_SCRIPT = "gsd-t-brevity-guard.js";
|
|
404
408
|
|
|
405
|
-
function
|
|
406
|
-
ensureDir(SCRIPTS_DIR);
|
|
407
|
-
const src = path.join(PKG_SCRIPTS, BREVITY_GUARD_SCRIPT);
|
|
408
|
-
const dest = path.join(SCRIPTS_DIR, BREVITY_GUARD_SCRIPT);
|
|
409
|
-
if (!fs.existsSync(src)) { warn("Brevity-guard script not found in package — skipping"); return; }
|
|
410
|
-
|
|
411
|
-
const srcContent = fs.readFileSync(src, "utf8");
|
|
412
|
-
const destContent = fs.existsSync(dest) ? fs.readFileSync(dest, "utf8") : "";
|
|
413
|
-
if (normalizeEol(srcContent) !== normalizeEol(destContent)) {
|
|
414
|
-
copyFile(src, dest, BREVITY_GUARD_SCRIPT);
|
|
415
|
-
} else {
|
|
416
|
-
info("Brevity-guard script unchanged");
|
|
417
|
-
}
|
|
418
|
-
|
|
409
|
+
function uninstallBrevityGuardHook() {
|
|
419
410
|
const parsed = readSettingsJson();
|
|
420
411
|
if (parsed === null && fs.existsSync(SETTINGS_JSON)) {
|
|
421
|
-
warn("settings.json has invalid JSON — cannot
|
|
412
|
+
warn("settings.json has invalid JSON — cannot remove brevity-guard hook");
|
|
422
413
|
return;
|
|
423
414
|
}
|
|
424
415
|
const settings = parsed || {};
|
|
425
|
-
if (!settings.hooks
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
416
|
+
if (!settings.hooks || !Array.isArray(settings.hooks.Stop)) {
|
|
417
|
+
info("Reader Contract active every turn (no legacy brevity-guard hook to remove)");
|
|
418
|
+
return;
|
|
419
|
+
}
|
|
420
|
+
const before = settings.hooks.Stop.length;
|
|
421
|
+
settings.hooks.Stop = settings.hooks.Stop.filter((entry) =>
|
|
422
|
+
!(entry.hooks && entry.hooks.some((h) => h.command && h.command.includes(BREVITY_GUARD_SCRIPT)))
|
|
430
423
|
);
|
|
431
|
-
if (
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
settings.hooks.Stop.push({ matcher: "", hooks: [{ type: "command", command: cmd }] });
|
|
436
|
-
|
|
424
|
+
if (settings.hooks.Stop.length === before) {
|
|
425
|
+
info("Reader Contract active every turn (no legacy brevity-guard hook present)");
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
437
428
|
if (!isSymlink(SETTINGS_JSON)) {
|
|
438
429
|
fs.writeFileSync(SETTINGS_JSON, JSON.stringify(settings, null, 2));
|
|
439
|
-
success("
|
|
430
|
+
success("Removed legacy brevity-guard Stop hook — Reader Contract now applies every turn");
|
|
440
431
|
} else {
|
|
441
432
|
warn("Skipping settings.json write — target is a symlink");
|
|
442
433
|
}
|
|
@@ -1660,8 +1651,8 @@ async function doInstall(opts = {}) {
|
|
|
1660
1651
|
heading("Heartbeat (Real-time Events)");
|
|
1661
1652
|
installHeartbeat();
|
|
1662
1653
|
|
|
1663
|
-
heading("
|
|
1664
|
-
|
|
1654
|
+
heading("Reader Contract (M93 — concise replies, every turn)");
|
|
1655
|
+
uninstallBrevityGuardHook();
|
|
1665
1656
|
|
|
1666
1657
|
heading("Update Check (Session Start)");
|
|
1667
1658
|
installUpdateCheck();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tekyzinc/gsd-t",
|
|
3
|
-
"version": "4.9.
|
|
3
|
+
"version": "4.9.14",
|
|
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",
|
|
@@ -26,6 +26,21 @@
|
|
|
26
26
|
const fs = require("fs");
|
|
27
27
|
const path = require("path");
|
|
28
28
|
|
|
29
|
+
// Reader Contract — emitted every turn so the standard is in front of the model
|
|
30
|
+
// each response (not a selective after-the-fact gate). Concise + jargon-free by
|
|
31
|
+
// its own rule. Generic before/after examples teach the shape.
|
|
32
|
+
const READER_CONTRACT = [
|
|
33
|
+
"[GSD-T READER CONTRACT] Before sending ANY reply, assume your first draft is too wordy and rewrite it tight. Rules:",
|
|
34
|
+
"• Answer FIRST. No preamble, no restating the question, no narrating what you're about to do (\"let me check…\"). Do the work silently, then give the result.",
|
|
35
|
+
"• Exception — when you're about to CHANGE code/files: state intent in one line first, so the user can stop a wrong direction.",
|
|
36
|
+
"• Gloss every code/jargon term in plain words on first use. No bare IDs or acronyms the reader must decode.",
|
|
37
|
+
"• Bullets/tables over paragraphs. Cut hedging and meta-commentary. Expand only if asked.",
|
|
38
|
+
"EXAMPLES (before → after):",
|
|
39
|
+
"• \"That's a great question, and it touches on something subtle. Let me look into how the cache works before I answer…\" → \"The cache lives in memory, cleared on restart.\"",
|
|
40
|
+
"• \"There are a few moving parts here. First, I want to make sure I understand the goal, because X has a gotcha…\" → \"Set X in .env. Gotcha: also add the localhost redirect URI or it rejects.\"",
|
|
41
|
+
"• \"Good catch — I conflated two things. Here's the honest correction: the files actually stack rather than overwrite…\" → \"You're right — files stack, they don't overwrite.\"",
|
|
42
|
+
].join("\n");
|
|
43
|
+
|
|
29
44
|
/**
|
|
30
45
|
* Resolve the active model profile from .gsd-t/model-profile.json.
|
|
31
46
|
*
|
|
@@ -123,6 +138,12 @@ process.stdin.on("end", () => {
|
|
|
123
138
|
// [GSD-T NOW] format is date-guard-invariant: NEVER alter it.
|
|
124
139
|
process.stdout.write(`[GSD-T NOW] ${liveTimestamp()}\n`);
|
|
125
140
|
|
|
141
|
+
// Reader Contract — injected EVERY turn, EVERY project (M93). The deterministic
|
|
142
|
+
// brevity gate was retired (it could only ever be selective); this puts the
|
|
143
|
+
// standard in front of the model each turn instead. Assume the first draft is
|
|
144
|
+
// wordy; rewrite it tight before sending.
|
|
145
|
+
process.stdout.write(READER_CONTRACT + "\n");
|
|
146
|
+
|
|
126
147
|
// Parse stdin ONCE; both the profile token and auto-route reuse it.
|
|
127
148
|
let data = null;
|
|
128
149
|
try { data = JSON.parse(input); } catch (_) { /* tolerated — gates below skip */ }
|
|
@@ -44,7 +44,7 @@ Two reply shapes, picked by what the user wants. **Discriminator: are you modify
|
|
|
44
44
|
- **Gloss jargon.** Never a bare code/acronym (e.g. `S2-M7` = section 2, milestone 7; `HC-003` = a rule ID) — say what it means in plain words on first use.
|
|
45
45
|
- **Format.** Bullets/tables over paragraphs; expand only on request; the dated banner stays (first line, always).
|
|
46
46
|
|
|
47
|
-
**
|
|
47
|
+
**Applied to EVERY reply, not selectively.** Assume your first draft is too wordy and rewrite it tight before sending — do not wait to be told. The `[GSD-T READER CONTRACT]` block (injected every turn by the UserPromptSubmit hook) restates this each response. (The old pattern-matching brevity Stop hook was retired: a gate that matches known-bad phrasings can only ever be selective and misses every new wording. The fix is to hold the standard for ALL output, every turn.)
|
|
48
48
|
|
|
49
49
|
|
|
50
50
|
# GSD-T: Contract-Driven Development
|
|
@@ -102,7 +102,21 @@ async function runCli(projectDir, subcmd, argv, localBin, label, parseJson = tru
|
|
|
102
102
|
].join("\n");
|
|
103
103
|
const opts = { label, schema: _CLI_ENVELOPE_SCHEMA, model: "haiku" };
|
|
104
104
|
if (phaseNameOpt) opts.phase = phaseNameOpt;
|
|
105
|
-
|
|
105
|
+
// The CLI is run by a haiku helper agent, not a subprocess, so a return can transiently
|
|
106
|
+
// come back missing its parsed result. Retry ONCE on a missing/unparsed result before
|
|
107
|
+
// giving up — a genuine CLI failure fails both attempts (real error survives the retry),
|
|
108
|
+
// while a transient helper miss is recovered. Only retry when JSON was expected (parseJson)
|
|
109
|
+
// and the parsed result is absent; never retry on a clean exit that simply returned no JSON.
|
|
110
|
+
const runOnce = () => agent(prompt, opts).catch((e) => ({ ok: false, exitCode: -1, envelope: null, stderr: String(e && e.message), via: "error" }));
|
|
111
|
+
let r = await runOnce();
|
|
112
|
+
// Retry once when JSON was expected but no parsed result came back — covers both the
|
|
113
|
+
// throw path (via="error") and a malformed return the loose schema let through (ok=false
|
|
114
|
+
// with the result absent). A real CLI failure that returned valid JSON (envelope present,
|
|
115
|
+
// ok=false) is NOT retried — that is a true result, not a transient miss.
|
|
116
|
+
const missingResult = (x) => !x || (parseJson && (x.envelope === undefined || x.envelope === null) && x.ok !== true);
|
|
117
|
+
if (missingResult(r)) {
|
|
118
|
+
r = await runOnce();
|
|
119
|
+
}
|
|
106
120
|
return r || { ok: false, exitCode: -1, envelope: null, via: "error" };
|
|
107
121
|
}
|
|
108
122
|
async function runPreflight(projectDir, label = "preflight", phaseNameOpt) { return runCli(projectDir, "preflight", ["--json"], "cli-preflight.cjs", label, true, phaseNameOpt); }
|