@tekyzinc/gsd-t 5.11.23 → 5.11.24

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.24] - 2026-08-11
6
+
7
+ ### Fixed — the code graph never reached a single scanning agent for six weeks
8
+
9
+ `graphWiringMode` was set to `"WIRED"` and tested with `=== "wired"`. Case
10
+ sensitive, so never true: the structural slice — dead-code candidates, dangling
11
+ references, clusters — was computed on every scan and then discarded, unused,
12
+ from **2026-06-30 to 2026-08-11**.
13
+
14
+ Nothing looked wrong. The register header prints the same variable, so every
15
+ scan honestly reported `WIRED`: the graph really had built and answered. Its
16
+ results simply never arrived where they were needed. Every scan in those six
17
+ weeks ran graph-blind while reporting the graph as wired.
18
+
19
+ **It was the second time the same variable broke this way.** The producer was
20
+ uppercased in M99 to satisfy a metrics rollup comparing `=== "WIRED"`, and that
21
+ fix broke the scan's own consumer. Matching one casing to another moves the bug;
22
+ both ends now compare without case, which is the house rule for a value like
23
+ this anyway.
24
+
25
+ The M99 test that required exact casing is **superseded** — it locked in the
26
+ requirement that caused the outage it was written to prevent. Replaced by a
27
+ guard on the property that actually matters: a mode the scan emits is a mode
28
+ every reader recognises, however it was typed.
29
+
30
+ - `templates/workflows/gsd-t-scan.workflow.js`: the consumer, and the comment that told the next maintainer to match casing
31
+ - `bin/gsd-t-graph-metrics-rollup.cjs`: all three mode comparisons
32
+ - `test/m112-graph-wiring-casing.test.js`: 6 tests, including that no reader compares case-sensitively
33
+ - `test/m99-graph-metrics-rollup.test.js`: the old casing guard replaced
34
+
5
35
  ## [5.11.23] - 2026-08-10
6
36
 
7
37
  ### Fixed — the scan could silently leave code out, and slices were too large to read
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # GSD-T: Contract-Driven Development for Claude Code
2
2
 
3
- **v5.11.23** - A methodology for reliable, parallelizable development using Claude Code with optional Agent Teams support.
3
+ **v5.11.24** - 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.
@@ -337,14 +337,17 @@ function rollup(projectRoot) {
337
337
  byConsumer[consumer].wiringCount++;
338
338
 
339
339
  const mode = ev.graphWiringMode || "";
340
- if (mode === "WIRED") {
340
+ // Case-insensitive: matching an exact casing here is what started the
341
+ // six-week outage — the producer was uppercased to satisfy this line
342
+ // and the scan's own consumer (which tested lowercase) went dead.
343
+ if (String(mode).toLowerCase() === "wired") {
341
344
  l2cWiredCount++;
342
- } else if (mode === "fallback-announced") {
345
+ } else if (String(mode).toLowerCase() === "fallback-announced") {
343
346
  l2cFallbackCount++;
344
347
  // Record minute-bucket for co-occurrence check
345
348
  if (!fallbackBuckets[consumer]) fallbackBuckets[consumer] = new Set();
346
349
  fallbackBuckets[consumer].add(_minuteBucket(ev.ts));
347
- } else if (mode === "disabled") {
350
+ } else if (String(mode).toLowerCase() === "disabled") {
348
351
  l2cDisabledCount++;
349
352
  }
350
353
  break;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tekyzinc/gsd-t",
3
- "version": "5.11.23",
3
+ "version": "5.11.24",
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",
@@ -675,15 +675,16 @@ if (graphMode === "disabled") {
675
675
  },
676
676
  tier: (deadCodeResult && deadCodeResult.tier) || (danglingResult && danglingResult.tier) || "unknown",
677
677
  };
678
- // M99 Red Team fix: emit "WIRED" (uppercase) to MATCH the --auto router
679
- // producers (bin/gsd-t.js:3915) AND the rollup's comparison
680
- // (gsd-t-graph-metrics-rollup.cjs:333 `=== "WIRED"`). Lowercase "wired" here
681
- // fell through all three rollup branches a successfully-wired scan reported
682
- // WIRED:0, defeating success criterion 13 (the metric M99 exists to surface).
683
- // [RULE] wiring-mode-casing-matches-rollup
678
+ // The casing here no longer matters: every reader of this value compares
679
+ // without case (2026-08-11). It was uppercased in M99 to satisfy a rollup
680
+ // that tested `=== "WIRED"`, and that broke the scan's own consumer, which
681
+ // tested `=== "wired"` the structural slice was silently discarded for
682
+ // six weeks. Chasing one casing to match another is what caused both
683
+ // outages; the readers were fixed instead.
684
+ // [RULE] wiring-mode-compared-without-case
684
685
  graphWiringMode = "WIRED";
685
686
  log(`graph-wiring: WIRED — structural slice ready (dead-code: ${structuralSlice.deadCode.length} candidates, dangling: ${structuralSlice.dangling.length} edges, clusters: ${structuralSlice.clusters.length} groups, tier: ${structuralSlice.tier}). Slice will be INJECTED ADDITIVELY into scanSlice deep-finders. [RULE] scan-injects-structural-slice`);
686
- await persistWiringMode("WIRED"); // M99 D2 [RULE] wiring-mode-three-states / wiring-mode-casing-matches-rollup
687
+ await persistWiringMode("WIRED"); // M99 D2 [RULE] wiring-mode-three-states / wiring-mode-compared-without-case
687
688
  }
688
689
  }
689
690
  }
@@ -921,7 +922,18 @@ async function scanSlice(slice) {
921
922
  // M94-D6: inject the structural slice context ADDITIVELY — only when graph is wired.
922
923
  // When graphMode==="disabled" OR graph-unavailable, graphSliceContext is null (no injection).
923
924
  // [RULE] scan-injects-structural-slice / [RULE] no-graph-baseline-proven-graph-free
924
- const graphSliceContext = (graphWiringMode === "wired" && structuralSlice) ? structuralSlice : null;
925
+ // Compared case-INSENSITIVELY. This test read `=== "wired"` while the producer
926
+ // wrote "WIRED", so it was never true: the structural slice was computed and
927
+ // then discarded, unused, from 2026-06-30 until 2026-08-11. Every scan in
928
+ // those six weeks ran graph-blind while logging WIRED — the header was not
929
+ // lying (the graph DID build and answer), its results simply never arrived.
930
+ //
931
+ // The producer was uppercased to fix a metrics rollup that compared to
932
+ // "WIRED"; that fix broke this consumer. Matching the casing again would just
933
+ // move the bug a third time, so both ends now compare without case — which is
934
+ // the house rule for a value like this in the first place.
935
+ const graphSliceContext =
936
+ (String(graphWiringMode).toLowerCase() === "wired" && structuralSlice) ? structuralSlice : null;
925
937
  const finderResult = await runFinder(slice, graphSliceContext);
926
938
  // M72: distinguish a FAILED finder (null after retries) from a genuinely-clean slice.
927
939
  if (!finderResult || !Array.isArray(finderResult.findings)) {