@tekyzinc/gsd-t 4.9.11 → 4.9.13
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/scripts/gsd-t-brevity-guard.js +44 -4
- package/templates/CLAUDE-global.md +1 -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.13] - 2026-06-25 (M93 — Reader Contract every turn, retire the selective gate — patch)
|
|
6
|
+
|
|
7
|
+
### Changed — concise output is now enforced on EVERY reply, not by pattern-matching
|
|
8
|
+
|
|
9
|
+
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.
|
|
10
|
+
|
|
11
|
+
- **Retired the brevity-guard Stop hook.** `gsd-t update-all` now *removes* it from any install that still has it.
|
|
12
|
+
- **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.
|
|
13
|
+
- 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).
|
|
14
|
+
|
|
15
|
+
## [4.9.12] - 2026-06-23 (M93 brevity-guard live-tuning — patch)
|
|
16
|
+
|
|
17
|
+
### Changed — brevity guard catches more, false-blocks less
|
|
18
|
+
|
|
19
|
+
Live use surfaced two gaps in the M93 brevity-guard Stop hook (it missed a real verbose reply, then would have false-blocked a clean one):
|
|
20
|
+
|
|
21
|
+
- **Interleaved preamble detection** — the guard previously only caught process-narration *stacked at the very front*. It now also catches narration/meta-commentary *scattered through the opening* (ack → narrate → a little content → narrate again → over-explain), which is the real exhausting shape. Conservative: scans only the first 6 sentences, needs ≥2 hits.
|
|
22
|
+
- **Narrowed live-reply jargon scope** — the hook no longer forces a gloss on a bare milestone code like `M92` (established conversational shorthand once context exists — it was false-blocking clean answers). It still forces glosses on the genuinely opaque IDs (`HC-003`, `S2-M7`). The stricter rule stays in the document lint (`gsd-t-jargon-lint.cjs`), where a reader has no conversation for context.
|
|
23
|
+
|
|
5
24
|
## [4.9.11] - 2026-06-23 (M93 — Brevity Guard: enforce concise, answer-first output — patch)
|
|
6
25
|
|
|
7
26
|
### Added — conciseness is now enforced, not just requested
|
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.13** - 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.13",
|
|
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 */ }
|
|
@@ -63,15 +63,28 @@ const ALLOWLIST = new Set([
|
|
|
63
63
|
]);
|
|
64
64
|
|
|
65
65
|
// High-signal code-token patterns (bare, unglossed → block on first use).
|
|
66
|
+
// LIVE-REPLY scope only: limited to the genuinely opaque IDs the user can't decode
|
|
67
|
+
// (contract / hard-constraint / stage-milestone codes). A bare `M92` milestone ref
|
|
68
|
+
// is established conversational shorthand once context exists — forcing a gloss on
|
|
69
|
+
// it every turn is over-strict and false-blocks clean answers. The stricter
|
|
70
|
+
// `M\d+-D\d+`-and-bare-`M\d+` rule stays in the DOC lint (bin/gsd-t-jargon-lint.cjs),
|
|
71
|
+
// where a reader has no conversational context. (M93 live-tuning, 2026-06-23.)
|
|
66
72
|
const JARGON_PATTERNS = [
|
|
67
|
-
/\bS2-M\d+\b/, // S2-M7
|
|
68
|
-
/\bHC-\d+\b/, // HC-003
|
|
69
|
-
/\bM\d+(?:-D\d+(?:-T\d+)?)?\b/, // M92, M92-D1, M92-D1-T3
|
|
73
|
+
/\bS2-M\d+\b/, // S2-M7 (stage-milestone code)
|
|
74
|
+
/\bHC-\d+\b/, // HC-003 (hard-constraint / contract id)
|
|
70
75
|
];
|
|
71
76
|
|
|
72
77
|
// Narration openers: first-person "about to" framing — intent without content.
|
|
73
78
|
const NARRATION_OPENER =
|
|
74
|
-
/^(let me|before i\b|i'?ll\b|i'?m going to|i am going to|first,?\s+let me|i want to|i need to|let's|let us|going to)\b/i;
|
|
79
|
+
/^(let me|before i\b|i'?ll\b|i'?m going to|i am going to|first,?\s+let me|i want to|i need to|let's|let us|going to|i need to be|i have to|i should|i want to be|let me untangle|i'?m going to verify)\b/i;
|
|
80
|
+
|
|
81
|
+
// Meta-commentary / hedge openers — sentences that talk ABOUT the answer instead
|
|
82
|
+
// of giving it ("the honest correction:", "here's the real picture", "good catch —
|
|
83
|
+
// and I…", "where does that leave you"). These don't match NARRATION_OPENER but are
|
|
84
|
+
// the same preamble disease — over-explaining around the answer. Counted in the
|
|
85
|
+
// interleaved scan, NOT the leading scan (they appear mid-reply).
|
|
86
|
+
const META_OPENER =
|
|
87
|
+
/^(good (catch|question|point|instinct)|fair (challenge|point)|the honest|here'?s (the|what)|so,? (where|to|that|let)|that'?s (a |the )?(real|fair|honest)|the (real |accurate )?(picture|version|correction|nuance)|to (untangle|be (clear|precise|careful|honest))|i (conflated|misspoke|need to be|want to be))\b/i;
|
|
75
88
|
|
|
76
89
|
// ── Transcript tail-scan (REUSED from gsd-t-conversation-capture.js) ─────────
|
|
77
90
|
// Pull the most recent assistant turn from a Claude Code transcript JSONL by
|
|
@@ -223,6 +236,21 @@ function _leadingNarrationCount(prose) {
|
|
|
223
236
|
return count;
|
|
224
237
|
}
|
|
225
238
|
|
|
239
|
+
// Count narration + meta-commentary sentences INTERLEAVED in the opening region
|
|
240
|
+
// (the first WINDOW sentences), regardless of contiguity. Catches the real failure
|
|
241
|
+
// shape the leading-only scan misses: an ack, then narration, then a content
|
|
242
|
+
// sentence, then MORE narration ("Good catch… Let me untangle… What I said was X…
|
|
243
|
+
// Let me verify…"). Conservative: only scans the opening window, needs >=2 hits.
|
|
244
|
+
const INTERLEAVED_WINDOW = 6;
|
|
245
|
+
function _interleavedPreambleCount(prose) {
|
|
246
|
+
const sentences = _sentences(prose).slice(0, INTERLEAVED_WINDOW);
|
|
247
|
+
let count = 0;
|
|
248
|
+
for (const s of sentences) {
|
|
249
|
+
if (NARRATION_OPENER.test(s) || META_OPENER.test(s)) count++;
|
|
250
|
+
}
|
|
251
|
+
return count;
|
|
252
|
+
}
|
|
253
|
+
|
|
226
254
|
// Find a bare unglossed high-signal jargon token (first occurrence, no gloss).
|
|
227
255
|
// A gloss = parenthetical or quote in the SAME sentence containing the token.
|
|
228
256
|
function _findBareJargon(prose) {
|
|
@@ -268,6 +296,18 @@ function detect(text, mode) {
|
|
|
268
296
|
};
|
|
269
297
|
}
|
|
270
298
|
|
|
299
|
+
// Interleaved preamble: narration/meta-commentary scattered through the opening
|
|
300
|
+
// (not just stacked at the front). The real exhausting shape: ack → narrate →
|
|
301
|
+
// a bit of content → narrate again → over-explain.
|
|
302
|
+
const interleaved = _interleavedPreambleCount(prose);
|
|
303
|
+
if (interleaved >= 2) {
|
|
304
|
+
return {
|
|
305
|
+
block: true,
|
|
306
|
+
reason: "Answer-first: the opening has " + interleaved +
|
|
307
|
+
" preamble/meta sentences (narration or 'here's the real picture'-style framing) around the answer. Lead with the answer; cut the meta-commentary.",
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
|
|
271
311
|
const jargon = _findBareJargon(prose);
|
|
272
312
|
if (jargon) {
|
|
273
313
|
return {
|
|
@@ -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
|