@tekyzinc/gsd-t 5.11.26 → 5.11.27
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 +34 -0
- package/README.md +1 -1
- package/package.json +1 -1
- package/templates/workflows/gsd-t-scan.workflow.js +106 -4
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to GSD-T are documented here. Updated with each release.
|
|
4
4
|
|
|
5
|
+
## [5.11.27] - 2026-08-11
|
|
6
|
+
|
|
7
|
+
### Fixed — the scan's volume probe returned a stand-in, and the whole run was built on it
|
|
8
|
+
|
|
9
|
+
HiloAviation, this morning. The probe answered `totals: {"trackedFiles": 5036}`
|
|
10
|
+
and `slices: [{"key": "test", "paths": ["src/"]}]` — one slice for a 4,900-file
|
|
11
|
+
application, keyed "test", and none of the six measurements the prompt asked for.
|
|
12
|
+
The schema accepted it (one slice is legal, `totals` takes any object), so the
|
|
13
|
+
scan read that single slice, produced 3 findings, and headed the register
|
|
14
|
+
"Coverage: FULL".
|
|
15
|
+
|
|
16
|
+
The probe had no retry. Every finder gets re-run when it answers `{"findings":[]}`;
|
|
17
|
+
the probe — which decides what every finder will ever look at — got one call, and
|
|
18
|
+
whatever came back became the plan.
|
|
19
|
+
|
|
20
|
+
The tell is not the slice count; a small repo really is one slice. It is that the
|
|
21
|
+
fields explicitly requested are absent: the shape of an answer with none of the
|
|
22
|
+
work behind it. So the result is now inspected before it is trusted, retried on
|
|
23
|
+
opus with the fault named, and a second stand-in HALTS the scan rather than
|
|
24
|
+
scanning it — a register that under-counts while claiming full coverage is worse
|
|
25
|
+
than no register.
|
|
26
|
+
|
|
27
|
+
- `templates/workflows/gsd-t-scan.workflow.js`: `probePlaceholderFaults()` +
|
|
28
|
+
retry-on-opus + halt; the prompt is now a reusable constant so the retry sends
|
|
29
|
+
the same task; the slice count and totals are logged at probe time, so a thin
|
|
30
|
+
plan is visible before the run rather than inferred afterwards from a thin
|
|
31
|
+
register.
|
|
32
|
+
- `test/m112-probe-placeholder.test.js`: 11 regressions, including the verbatim
|
|
33
|
+
payload that produced the 3-finding scan.
|
|
34
|
+
|
|
35
|
+
The whole-tree tell only fires when the measurements are missing too. A small
|
|
36
|
+
project genuinely is one slice covering `src/`, and halting that scan would be a
|
|
37
|
+
false alarm on every small repo — worse than the bug being fixed.
|
|
38
|
+
|
|
5
39
|
## [5.11.26] - 2026-08-11
|
|
6
40
|
|
|
7
41
|
### Fixed — a quarter of every import edge pointed at a string no file matched
|
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.27** - 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.27",
|
|
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",
|
|
@@ -399,7 +399,7 @@ log(`preflight ok — branch=${pre.branch}, repo=${repoName}, priorRegister=${pr
|
|
|
399
399
|
|
|
400
400
|
// Volume probe — an agent measures the codebase (its own Bash) and carves slices.
|
|
401
401
|
phase("Probe");
|
|
402
|
-
const
|
|
402
|
+
const PROBE_PROMPT =
|
|
403
403
|
[
|
|
404
404
|
`⛔ TARGET DIRECTORY IS FIXED: you MUST scan ONLY the project at the absolute path \`${projectDir}\`. Before any measurement, \`cd ${projectDir}\` (or pass that exact path to every Read/Grep/Bash). Do NOT scan your current working directory, the GSD-T package, or any other tree — every file you measure and every slice path you emit MUST be under \`${projectDir}\`. If \`${projectDir}\` does not exist or is empty, return a single slice noting that.`,
|
|
405
405
|
``,
|
|
@@ -437,14 +437,116 @@ const probe = await agent(
|
|
|
437
437
|
``,
|
|
438
438
|
`Measure with real tooling and report in \`totals\`: files, loc, routes, tables, components, featureDomains (distinct business/feature areas). Read \`${projectDir}/package.json\` for the stack. Return JSON per the schema: totals + slices.`,
|
|
439
439
|
SHAPE_RULE,
|
|
440
|
-
].join("\n")
|
|
441
|
-
|
|
442
|
-
|
|
440
|
+
].join("\n");
|
|
441
|
+
|
|
442
|
+
// Did the probe MEASURE the codebase, or return something shaped like an answer?
|
|
443
|
+
//
|
|
444
|
+
// [RULE] probe-placeholder-is-rejected-not-scanned
|
|
445
|
+
//
|
|
446
|
+
// HiloAviation, 2026-08-11. The probe returned:
|
|
447
|
+
//
|
|
448
|
+
// totals : {"trackedFiles": 5036}
|
|
449
|
+
// slices : [{"key": "test", "paths": ["src/"]}]
|
|
450
|
+
//
|
|
451
|
+
// One slice for a 4,900-file application, keyed "test", and a totals object
|
|
452
|
+
// carrying none of the six numbers the prompt asked for. The schema accepted it
|
|
453
|
+
// — one slice is legal, and totals takes any object — so the scan ran that one
|
|
454
|
+
// slice, found 3 findings, and the register's header read "Coverage: FULL".
|
|
455
|
+
//
|
|
456
|
+
// The tell is not the slice count. It is that the fields explicitly named in the
|
|
457
|
+
// prompt are simply absent: the agent produced the SHAPE of an answer without
|
|
458
|
+
// doing the work, the same instinct that sends `{"findings":[]}` through a
|
|
459
|
+
// finder. So the probe result is inspected before it is trusted, and a probe
|
|
460
|
+
// that did not measure is retried on a stronger model rather than scanned.
|
|
461
|
+
function probePlaceholderFaults(result) {
|
|
462
|
+
const faults = [];
|
|
463
|
+
const totals = (result && result.totals) || {};
|
|
464
|
+
const slices = (result && Array.isArray(result.slices) && result.slices) || [];
|
|
465
|
+
|
|
466
|
+
// Every number the prompt asks for by name. None present means the agent
|
|
467
|
+
// reported whatever it had to hand instead of measuring.
|
|
468
|
+
const asked = ["files", "loc", "routes", "tables", "components", "featureDomains"];
|
|
469
|
+
const present = asked.filter((k) => totals[k] !== undefined && totals[k] !== null);
|
|
470
|
+
if (present.length === 0) {
|
|
471
|
+
faults.push(
|
|
472
|
+
`totals contains none of the six requested measurements (${asked.join(", ")}) — it holds ` +
|
|
473
|
+
`${JSON.stringify(totals).slice(0, 160)}`
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
// A single slice whose path is the whole source tree has decomposed nothing —
|
|
478
|
+
// but ONLY when the totals are missing too. A small repo genuinely is one
|
|
479
|
+
// slice covering src/, and halting that scan would be a false alarm on every
|
|
480
|
+
// small project. What separates the two is whether the agent measured: a real
|
|
481
|
+
// probe reports the numbers AND explains the single slice. Both tells together
|
|
482
|
+
// are one judgment, not two independent ones.
|
|
483
|
+
const WHOLE_TREE = new Set([".", "./", "*", "src", "src/", "./src", "./src/", projectDir]);
|
|
484
|
+
if (slices.length === 1 && present.length === 0) {
|
|
485
|
+
const paths = Array.isArray(slices[0].paths) ? slices[0].paths : [];
|
|
486
|
+
if (paths.length && paths.every((p) => WHOLE_TREE.has(String(p).trim().replace(/\/+$/, "/")) || WHOLE_TREE.has(String(p).trim()))) {
|
|
487
|
+
faults.push(
|
|
488
|
+
`the one slice "${slices[0].key}" owns the entire source tree (${JSON.stringify(paths)}) — ` +
|
|
489
|
+
`that is not a decomposition`
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
return faults;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
let probe = await agent(PROBE_PROMPT, {
|
|
498
|
+
label: "volume-probe", phase: "Probe", schema: PROBE_SCHEMA, model: "sonnet",
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
let probeFaults = probePlaceholderFaults(probe);
|
|
502
|
+
if (probeFaults.length) {
|
|
503
|
+
log(`⚠ PROBE DID NOT MEASURE — the answer has the right shape but not the work behind it:`);
|
|
504
|
+
for (const f of probeFaults) log(` · ${f}`);
|
|
505
|
+
log(` retrying on opus with the fault named.`);
|
|
506
|
+
|
|
507
|
+
const retry = await agent(
|
|
508
|
+
[
|
|
509
|
+
PROBE_PROMPT,
|
|
510
|
+
``,
|
|
511
|
+
`!! A PREVIOUS ATTEMPT AT THIS EXACT TASK WAS REJECTED. What was wrong with it:`,
|
|
512
|
+
...probeFaults.map((f) => ` · ${f}`),
|
|
513
|
+
``,
|
|
514
|
+
`RUN THE MEASUREMENTS. Do not answer from a guess, and do not return a minimal stand-in to see whether it is accepted — it will not be.`,
|
|
515
|
+
`Count the files. Count the lines. Find the routes, the database tables, the components, and the distinct business areas. Those NUMBERS go in \`totals\`.`,
|
|
516
|
+
`Then carve the codebase into slices by business capability, one slice per real area of the product. A single slice owning the whole tree is not an answer.`,
|
|
517
|
+
].join("\n"),
|
|
518
|
+
{ label: "volume-probe (retry on opus)", phase: "Probe", schema: PROBE_SCHEMA, model: "opus" }
|
|
519
|
+
);
|
|
520
|
+
|
|
521
|
+
const retryFaults = probePlaceholderFaults(retry);
|
|
522
|
+
if (retry && Array.isArray(retry.slices) && retry.slices.length && retryFaults.length === 0) {
|
|
523
|
+
log(`✓ probe recovered on opus — ${retry.slices.length} slice(s), totals=${JSON.stringify(retry.totals)}`);
|
|
524
|
+
probe = retry;
|
|
525
|
+
} else {
|
|
526
|
+
// Both attempts produced a stand-in. Scanning it would read a fraction of the
|
|
527
|
+
// codebase and print "Coverage: FULL" over the result — a register that lies
|
|
528
|
+
// is worse than no register.
|
|
529
|
+
log(`✗ PROBE FAILED — two attempts, neither measured the codebase.`);
|
|
530
|
+
for (const f of (retryFaults.length ? retryFaults : probeFaults)) log(` · ${f}`);
|
|
531
|
+
return {
|
|
532
|
+
status: "failed",
|
|
533
|
+
reason: "probe-placeholder",
|
|
534
|
+
message:
|
|
535
|
+
"The probe never measured the codebase, on either attempt. Nothing was scanned: a run on this " +
|
|
536
|
+
"answer would have covered a fraction of the project while reporting full coverage.",
|
|
537
|
+
probe: retry || probe,
|
|
538
|
+
};
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
443
542
|
const rawSlices = (probe && Array.isArray(probe.slices) && probe.slices) || [];
|
|
444
543
|
if (!rawSlices.length) {
|
|
445
544
|
log("probe returned no slices — halting");
|
|
446
545
|
return { status: "failed", reason: "no-slices", probe };
|
|
447
546
|
}
|
|
547
|
+
// What the plan actually is, before anything runs on it. A thin plan was only
|
|
548
|
+
// ever visible afterwards, in a thin register.
|
|
549
|
+
log(`probe: ${rawSlices.length} slice(s) — totals=${JSON.stringify(probe.totals || {})}`);
|
|
448
550
|
// Did the probe slice the way it was told? A layer prefix is the tell.
|
|
449
551
|
//
|
|
450
552
|
// The instruction alone is not enough — a prompt is advice, and this axis flipped
|