@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 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.17** - A methodology for reliable, parallelizable development using Claude Code with optional Agent Teams support.
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.17",
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
- additionalProperties: false,
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
- additionalProperties: false,
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
- additionalProperties: false,
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
- if (!v || v.verdict === "false-positive" || v.confirmed === false) return null;
703
- return { ...f, severity: v.correctedSeverity || f.severity, _verify: v.verdict };
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
- const docsOk = docResults.filter(Boolean).filter((r) => r.status === "written" || r.status === "merged");
1135
- const docsFailed = docResults.filter(Boolean).filter((r) => r.status === "failed");
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) ───────────────────────────────────────────────