@tekyzinc/gsd-t 5.11.17 → 5.11.18
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 +30 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/templates/workflows/gsd-t-scan.workflow.js +31 -14
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,36 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to GSD-T are documented here. Updated with each release.
|
|
4
4
|
|
|
5
|
+
## [5.11.18] - 2026-08-10
|
|
6
|
+
|
|
7
|
+
### Fixed — a scan finding was thrown away over its FORM, not its truth
|
|
8
|
+
|
|
9
|
+
HiloAviation, a 228-slice deep scan: 2 slices failed after 179.6k tokens and 61
|
|
10
|
+
tool calls of real work. The agent's own log shows one call carrying findings
|
|
11
|
+
and the next carrying an empty array — an agent giving up and submitting nothing
|
|
12
|
+
to satisfy the schema. Both refused. The report then read 226 of 228 with no
|
|
13
|
+
sign that two of the densest areas had vanished.
|
|
14
|
+
|
|
15
|
+
Two rules did it, neither of which checks whether a finding is *true*:
|
|
16
|
+
|
|
17
|
+
- **No extra fields.** A finder adding `evidence` or a line number lost the
|
|
18
|
+
entire call, not the extra field.
|
|
19
|
+
- **Case-exact word lists.** "High" refused where "HIGH" was demanded, and
|
|
20
|
+
"High" refused where "high" was demanded — in the same finding.
|
|
21
|
+
|
|
22
|
+
Large projects hit this hardest: the failing slices were data-access and
|
|
23
|
+
repositories, the areas producing the most findings and the most extra context.
|
|
24
|
+
|
|
25
|
+
**Nothing about what a finding must contain is loosened.** Title, severity,
|
|
26
|
+
area, files, detail and recommendation are still required.
|
|
27
|
+
|
|
28
|
+
Widening a word list breaks exact-match comparisons, so three were fixed in the
|
|
29
|
+
same pass — most importantly the false-positive check, which would otherwise
|
|
30
|
+
have KEPT a finding the verifier had just rejected.
|
|
31
|
+
|
|
32
|
+
- `templates/workflows/gsd-t-scan.workflow.js`: agent-authored schemas accept extra keys and any casing; severity normalised once at the merge point; verdict and document-status comparisons made case-insensitive
|
|
33
|
+
- `test/m112-scan-schema-tolerance.test.js`: 8 tests, including that the required-field list was not quietly shortened
|
|
34
|
+
|
|
5
35
|
## [5.11.17] - 2026-08-10
|
|
6
36
|
|
|
7
37
|
### Added — `/concise`, rewrite the last reply short on request
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# GSD-T: Contract-Driven Development for Claude Code
|
|
2
2
|
|
|
3
|
-
**v5.11.
|
|
3
|
+
**v5.11.18** - 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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tekyzinc/gsd-t",
|
|
3
|
-
"version": "5.11.
|
|
3
|
+
"version": "5.11.18",
|
|
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",
|
|
@@ -112,7 +112,7 @@ const PROBE_SCHEMA = {
|
|
|
112
112
|
properties: {
|
|
113
113
|
key: { type: "string" },
|
|
114
114
|
paths: { type: "array", items: { type: "string" } },
|
|
115
|
-
dimension: { type: "string", enum: ["architecture", "business-rules", "security", "quality", "contracts", "feature-domain", "data-layer", "api-surface", "testing"] },
|
|
115
|
+
dimension: { type: "string", enum: ["architecture", "ARCHITECTURE", "Architecture", "business-rules", "BUSINESS-RULES", "Business-rules", "security", "SECURITY", "Security", "quality", "QUALITY", "Quality", "contracts", "CONTRACTS", "Contracts", "feature-domain", "FEATURE-DOMAIN", "Feature-domain", "data-layer", "DATA-LAYER", "Data-layer", "api-surface", "API-SURFACE", "Api-surface", "testing", "TESTING", "Testing"] },
|
|
116
116
|
why: { type: "string" },
|
|
117
117
|
},
|
|
118
118
|
},
|
|
@@ -124,7 +124,8 @@ const PROBE_SCHEMA = {
|
|
|
124
124
|
const FINDER_SCHEMA = {
|
|
125
125
|
type: "object",
|
|
126
126
|
required: ["slice", "findings"],
|
|
127
|
-
|
|
127
|
+
// Agent-authored, so extra keys are allowed here too — see the finding object.
|
|
128
|
+
additionalProperties: true,
|
|
128
129
|
properties: {
|
|
129
130
|
slice: { type: "string" },
|
|
130
131
|
findings: {
|
|
@@ -132,16 +133,21 @@ const FINDER_SCHEMA = {
|
|
|
132
133
|
items: {
|
|
133
134
|
type: "object",
|
|
134
135
|
required: ["title", "severity", "area", "files", "detail", "recommendation"],
|
|
135
|
-
|
|
136
|
+
// Extra keys are ALLOWED. A finder that adds `evidence` or a line number
|
|
137
|
+
// is giving more, not less — and rejecting the whole call for it threw
|
|
138
|
+
// away 179.6k tokens of real findings on two of 228 slices in a large
|
|
139
|
+
// scan (HiloAviation, 2026-08-10). The required keys are still
|
|
140
|
+
// required, so nothing about what a finding must contain is loosened.
|
|
141
|
+
additionalProperties: true,
|
|
136
142
|
properties: {
|
|
137
143
|
title: { type: "string" },
|
|
138
|
-
severity: { type: "string", enum: ["CRITICAL", "HIGH", "MEDIUM", "LOW"] },
|
|
144
|
+
severity: { type: "string", enum: ["CRITICAL", "critical", "Critical", "HIGH", "high", "High", "MEDIUM", "medium", "Medium", "LOW", "low", "Low"] },
|
|
139
145
|
area: { type: "string" },
|
|
140
146
|
files: { type: "array", items: { type: "string" } },
|
|
141
147
|
detail: { type: "string" },
|
|
142
148
|
impact: { type: "string" },
|
|
143
149
|
recommendation: { type: "string" },
|
|
144
|
-
confidence: { type: "string", enum: ["high", "medium", "low"] },
|
|
150
|
+
confidence: { type: "string", enum: ["high", "HIGH", "High", "medium", "MEDIUM", "Medium", "low", "LOW", "Low"] },
|
|
145
151
|
},
|
|
146
152
|
},
|
|
147
153
|
},
|
|
@@ -152,12 +158,13 @@ const FINDER_SCHEMA = {
|
|
|
152
158
|
const VERIFY_SCHEMA = {
|
|
153
159
|
type: "object",
|
|
154
160
|
required: ["confirmed", "verdict"],
|
|
155
|
-
|
|
161
|
+
// Agent-authored — extra context must not cost the whole verdict.
|
|
162
|
+
additionalProperties: true,
|
|
156
163
|
properties: {
|
|
157
164
|
confirmed: { type: "boolean" },
|
|
158
|
-
verdict: { type: "string", enum: ["confirmed", "false-positive", "needs-detail"] },
|
|
165
|
+
verdict: { type: "string", enum: ["confirmed", "CONFIRMED", "Confirmed", "false-positive", "FALSE-POSITIVE", "False-positive", "needs-detail", "NEEDS-DETAIL", "Needs-detail"] },
|
|
159
166
|
note: { type: "string" },
|
|
160
|
-
correctedSeverity: { type: "string", enum: ["CRITICAL", "HIGH", "MEDIUM", "LOW"] },
|
|
167
|
+
correctedSeverity: { type: "string", enum: ["CRITICAL", "critical", "Critical", "HIGH", "high", "High", "MEDIUM", "medium", "Medium", "LOW", "low", "Low"] },
|
|
161
168
|
},
|
|
162
169
|
};
|
|
163
170
|
|
|
@@ -173,7 +180,7 @@ const DOC_RESULT_SCHEMA = {
|
|
|
173
180
|
additionalProperties: false,
|
|
174
181
|
properties: {
|
|
175
182
|
doc: { type: "string" },
|
|
176
|
-
status: { type: "string", enum: ["written", "merged", "skipped", "failed"] },
|
|
183
|
+
status: { type: "string", enum: ["written", "WRITTEN", "Written", "merged", "MERGED", "Merged", "skipped", "SKIPPED", "Skipped", "failed", "FAILED", "Failed"] },
|
|
177
184
|
path: { type: "string" },
|
|
178
185
|
notes: { type: "string" },
|
|
179
186
|
},
|
|
@@ -184,7 +191,7 @@ const RENDER_SCHEMA = {
|
|
|
184
191
|
required: ["status"],
|
|
185
192
|
additionalProperties: false,
|
|
186
193
|
properties: {
|
|
187
|
-
status: { type: "string", enum: ["rendered", "skipped", "failed"] },
|
|
194
|
+
status: { type: "string", enum: ["rendered", "RENDERED", "Rendered", "skipped", "SKIPPED", "Skipped", "failed", "FAILED", "Failed"] },
|
|
188
195
|
outputPath: { type: "string" },
|
|
189
196
|
notes: { type: "string" },
|
|
190
197
|
},
|
|
@@ -699,8 +706,15 @@ async function scanSlice(slice) {
|
|
|
699
706
|
].join("\n"),
|
|
700
707
|
{ label: `verify:${sliceKey}`, phase: "Deep Scan", schema: VERIFY_SCHEMA, model: "sonnet" }
|
|
701
708
|
);
|
|
702
|
-
|
|
703
|
-
|
|
709
|
+
// Compared case-INSENSITIVELY: the schema now accepts "false-positive"
|
|
710
|
+
// in any casing, so an exact match would silently KEEP a finding the
|
|
711
|
+
// verifier had rejected.
|
|
712
|
+
const verdict = String(v && v.verdict || "").toLowerCase();
|
|
713
|
+
if (!v || verdict === "false-positive" || v.confirmed === false) return null;
|
|
714
|
+
// Severity is normalised to the shouted form here, once, so the report
|
|
715
|
+
// reads consistently no matter how a finder typed it.
|
|
716
|
+
const sev = String(v.correctedSeverity || f.severity || "").toUpperCase();
|
|
717
|
+
return { ...f, severity: sev, _verify: verdict };
|
|
704
718
|
} catch (e) {
|
|
705
719
|
return { ...f, _verify: "verify-errored" };
|
|
706
720
|
}
|
|
@@ -1131,8 +1145,11 @@ const docResults = await parallel(
|
|
|
1131
1145
|
}
|
|
1132
1146
|
})
|
|
1133
1147
|
);
|
|
1134
|
-
|
|
1135
|
-
|
|
1148
|
+
// Case-insensitive: the status word-list accepts any casing, so an exact match
|
|
1149
|
+
// would count a written document as neither written nor failed.
|
|
1150
|
+
const _status = (r) => String(r && r.status || "").toLowerCase();
|
|
1151
|
+
const docsOk = docResults.filter(Boolean).filter((r) => _status(r) === "written" || _status(r) === "merged");
|
|
1152
|
+
const docsFailed = docResults.filter(Boolean).filter((r) => _status(r) === "failed");
|
|
1136
1153
|
log(`document phase: ${docsOk.length}/${docTargets.length} written/merged${docsFailed.length ? `; ${docsFailed.length} failed (non-fatal): ${docsFailed.map((d) => d.doc).join(", ")}` : ""}`);
|
|
1137
1154
|
|
|
1138
1155
|
// ─── Plain-English phase (M78) ───────────────────────────────────────────────
|