@tekyzinc/gsd-t 3.18.17 → 3.19.0
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 +69 -0
- package/bin/gsd-t-parallel.cjs +184 -4
- package/bin/gsd-t-unattended.cjs +501 -297
- package/bin/gsd-t-worker-dispatch.cjs +211 -0
- package/bin/headless-auto-spawn.cjs +28 -1
- package/bin/m46-iter-proof.cjs +149 -0
- package/bin/m46-worker-proof.cjs +201 -0
- package/bin/spawn-plan-writer.cjs +1 -1
- package/commands/gsd-t-resume.md +32 -0
- package/commands/gsd-t-status.md +10 -0
- package/docs/architecture.md +16 -0
- package/docs/requirements.md +20 -0
- package/package.json +1 -1
- package/scripts/gsd-t-update-check.js +13 -4
package/docs/architecture.md
CHANGED
|
@@ -1059,3 +1059,19 @@ cache behaviour.
|
|
|
1059
1059
|
2. **Four-file synchronization**: Any command change requires updating README, GSD-T-README, CLAUDE-global template, and gsd-t-help. Manual process — no automated validation.
|
|
1060
1060
|
3. **Pre-Commit Gate unenforced**: Mental checklist in CLAUDE.md, not a git hook or CI check.
|
|
1061
1061
|
4. **Progress.md Decision Log growth**: Unbounded append-only log. May need periodic archival strategy for long-lived projects.
|
|
1062
|
+
|
|
1063
|
+
## M46 Worker Sub-Dispatch
|
|
1064
|
+
|
|
1065
|
+
`bin/gsd-t-worker-dispatch.cjs` lets an unattended supervisor worker iteration fan out its own file-disjoint tasks as concurrent sub-workers. The module's `dispatchWorkerTasks({projectDir, parentSessionId, tasks, maxParallel})` entry point gates on three conditions: `process.env.GSD_T_UNATTENDED_WORKER === '1'` (caller runs inside a supervisor-launched worker child), `tasks.length > 1`, and pairwise file-disjointness across the task set's `files` arrays. When all three hold, it emits a spawn-plan frame with `kind: 'unattended-worker-sub'` and delegates execution to `bin/gsd-t-parallel.cjs::runDispatch` — the same M44-verified instrument the in-session planner uses. When any condition fails it returns `{parallel: false, reason}` and the worker falls through to its existing serial path.
|
|
1066
|
+
|
|
1067
|
+
Architecturally the module is a **new consumer** of `runDispatch`, not a modifier. The in-session dispatch call site is byte-identical post-D2; the worker adapter is a sibling caller. This completes the three-layer unattended parallelism model: supervisor iterations run in parallel (M46 D1 iter-parallel), each iteration's worker may sub-dispatch its disjoint tasks (M46 D2), and within the orchestrator the `/gsd` router still fans out via `runDispatch` for in-session action turns (M44). All three layers share one dispatch instrument and one disjointness predicate.
|
|
1068
|
+
|
|
1069
|
+
Contract: `.gsd-t/contracts/headless-default-contract.md` v2.1.0 §Worker Sub-Dispatch.
|
|
1070
|
+
|
|
1071
|
+
## M46 Iteration-Parallel Supervisor
|
|
1072
|
+
|
|
1073
|
+
`bin/gsd-t-unattended.cjs` ships four helpers that scaffold iteration-level parallelism in the supervisor main loop: `_runOneIter(state, opts)` is the extract-method of the single-iter body (one `claude -p` dispatch, heartbeat bookkeeping, state mutation) and returns an `IterResult` envelope; `_computeIterBatchSize(state, opts)` is the mode-safety gate that returns `1` when `state.status === "verify-needed"`, when `state.milestoneBoundary === true`, when `state.status === "complete-milestone"`, or when the caller did not pass `opts.maxIterParallel` as a number, and otherwise returns `min(opts.maxIterParallel, remainingIters, 8)` with a hard ceiling of 8; `_runIterParallel(state, opts, iterFn, batchSize)` is the concurrent driver — it builds `batchSize` promises, awaits them with `Promise.allSettled` so a rejected slice does not cancel siblings, and maps rejections into `{status: "error", tasksDone: [], verifyNeeded: false, artifacts: [], error}` envelopes; `_reconcile(state, results)` folds the `IterResult[]` back into canonical state with append-only union on `completedTasks`, last-writer-wins on `status`, OR across `verifyNeeded`, append on `artifacts`, and an overwrite `lastBatch` metadata block.
|
|
1074
|
+
|
|
1075
|
+
The production main loop currently runs exactly one iter per pass (`batchSize === 1`) always, unless a caller explicitly threads `opts.maxIterParallel` as a number through `_computeIterBatchSize` — which today's supervisor CLI does not. The four helpers are exported via `module.exports.__test__` so the T7 unit suite and any future caller can exercise batched iteration deterministically, but iter-parallelism at this layer is **scaffolded, not engaged in production**. The gate is intentional: `_runOneIter` mutates shared `state` fields (`state.iter`, heartbeat bookkeeping, the `writeState` side effect) that are not safe to execute concurrently against the same state object. Backlog #24 tracks the follow-up to make `_runOneIter` state-clone-safe and lift the production gate so the supervisor CLI can set a non-1 default.
|
|
1076
|
+
|
|
1077
|
+
Contract: `.gsd-t/contracts/iter-parallel-contract.md` v1.0.0.
|
package/docs/requirements.md
CHANGED
|
@@ -664,3 +664,23 @@ Milestone 44 D8 delivers a right-side two-layer task panel in the dashboard and
|
|
|
664
664
|
|
|
665
665
|
Supporting contract:
|
|
666
666
|
- `.gsd-t/contracts/spawn-plan-contract.md` v1.0.0 — schema + writer/reader/updater protocol + silent-fail rules.
|
|
667
|
+
|
|
668
|
+
### M46 D2 Worker Sub-Dispatch
|
|
669
|
+
|
|
670
|
+
A supervisor worker iteration assigned more than one ready task MUST fan those tasks out as concurrent sub-workers via the M44-verified `runDispatch` instrument rather than running them serially, provided the task set is pairwise file-disjoint and the iteration is running inside an unattended worker child (`GSD_T_UNATTENDED_WORKER=1`). This closes the parallelism gap where unattended workers historically executed their assigned tasks one-at-a-time even when the disjointness precondition held, defeating the purpose of the M44 dispatch infrastructure for all unattended runs. The sub-dispatch path is a new consumer of `bin/gsd-t-parallel.cjs::runDispatch` — no changes to the in-session planner's call site, no new disjointness predicate, and no new dispatch logic. Distinguished in telemetry via the new spawn-plan `kind: 'unattended-worker-sub'` value.
|
|
671
|
+
|
|
672
|
+
Acceptance:
|
|
673
|
+
- `bin/gsd-t-worker-dispatch.cjs` exists and exports `dispatchWorkerTasks` plus the `_areFileDisjoint` helper and the `SPAWN_PLAN_KIND` constant (`'unattended-worker-sub'`).
|
|
674
|
+
- Unit + integration tests in `test/m46-d2-worker-subdispatch.test.js` pass (trigger-condition matrix, disjointness predicate, runDispatch delegation, spawn-plan frame emission, serial fallback on overlap/single-task).
|
|
675
|
+
- Proof measurement recorded in `.gsd-t/metrics/m46-worker-proof.json` shows a parallel-vs-serial speedup of at least **2.5×** on a representative file-disjoint worker iteration.
|
|
676
|
+
- `.gsd-t/contracts/headless-default-contract.md` v2.1.0 §Worker Sub-Dispatch present as the locked source of truth.
|
|
677
|
+
|
|
678
|
+
### M46 D1 Iteration Parallelism
|
|
679
|
+
|
|
680
|
+
The unattended supervisor main loop MUST expose iter-level parallelism machinery via four extracted helpers — `_runOneIter`, `_computeIterBatchSize`, `_runIterParallel`, `_reconcile` — so unit tests and future callers can exercise batched iteration deterministically, with `_runIterParallel` using `Promise.allSettled` so a single rejected slice does not cancel siblings and `_reconcile` merging `IterResult[]` into state with append-only `completedTasks`, last-writer-wins `status`, OR across `verifyNeeded`, append on `artifacts`, and overwrite `lastBatch` metadata. The production main loop default remains serial (`batchSize = 1` always, via `_computeIterBatchSize` returning `1` whenever `opts.maxIterParallel` is not a number) pending the state-clone-safety follow-up tracked in backlog #24 — that work must land before the supervisor CLI sets a non-1 default.
|
|
681
|
+
|
|
682
|
+
Acceptance:
|
|
683
|
+
- `_runOneIter`, `_computeIterBatchSize`, `_runIterParallel`, and `_reconcile` are all exported via `module.exports.__test__` on `bin/gsd-t-unattended.cjs`.
|
|
684
|
+
- Tests in `test/m46-d1-iter-parallel.test.js` pass (serial fallback, parallel batch, mode-safety gate, error isolation, state reconciliation).
|
|
685
|
+
- Proof speedup ≥ **3.0×** recorded in `.gsd-t/metrics/m46-iter-proof.json` — a synthetic `batchSize = 4` measurement of the `_runIterParallel` driver, not the production main loop (which remains serial until backlog #24 lands).
|
|
686
|
+
- `.gsd-t/contracts/iter-parallel-contract.md` v1.0.0 present as the locked source of truth.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tekyzinc/gsd-t",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.19.00",
|
|
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",
|
|
@@ -14,6 +14,15 @@ const CACHE_FILE = path.join(CLAUDE_DIR, ".gsd-t-update-check");
|
|
|
14
14
|
const CHANGELOG = "https://github.com/Tekyz-Inc/get-stuff-done-teams/blob/main/CHANGELOG.md";
|
|
15
15
|
const SEMVER_RE = /^\d+\.\d+\.\d+(-[a-zA-Z0-9.]+)?$/;
|
|
16
16
|
|
|
17
|
+
// Local-time date stamp prefix for the version banner: "Tue: Mar 26, 2026, "
|
|
18
|
+
// The trailing two spaces are intentional — separates the date from the [GSD-T*] tag.
|
|
19
|
+
function dateStamp(now = new Date()) {
|
|
20
|
+
const day = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][now.getDay()];
|
|
21
|
+
const mon = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
|
22
|
+
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][now.getMonth()];
|
|
23
|
+
return `${day}: ${mon} ${now.getDate()}, ${now.getFullYear()}, `;
|
|
24
|
+
}
|
|
25
|
+
|
|
17
26
|
function isNewer(a, b) {
|
|
18
27
|
const ap = a.split(".").map(Number);
|
|
19
28
|
const bp = b.split(".").map(Number);
|
|
@@ -47,9 +56,9 @@ function doAutoUpdate(latest, installed) {
|
|
|
47
56
|
const updated = fs.existsSync(VERSION_FILE)
|
|
48
57
|
? fs.readFileSync(VERSION_FILE, "utf8").trim()
|
|
49
58
|
: latest;
|
|
50
|
-
console.log(
|
|
59
|
+
console.log(`${dateStamp()}[GSD-T AUTO-UPDATE] v${installed} → v${updated}. Changelog: ${CHANGELOG}`);
|
|
51
60
|
} catch {
|
|
52
|
-
console.log(
|
|
61
|
+
console.log(`${dateStamp()}[GSD-T UPDATE] v${installed} — update available (v${installed} → v${latest}). Auto-update failed — run manually: /gsd-t-version-update-all. Changelog: ${CHANGELOG}`);
|
|
53
62
|
}
|
|
54
63
|
}
|
|
55
64
|
|
|
@@ -86,7 +95,7 @@ function run() {
|
|
|
86
95
|
if (cached && cached.latest && isNewer(cached.latest, installed)) {
|
|
87
96
|
doAutoUpdate(cached.latest, installed);
|
|
88
97
|
} else {
|
|
89
|
-
console.log(
|
|
98
|
+
console.log(`${dateStamp()}[GSD-T] v${installed} — CURRENT. Changelog: ${CHANGELOG}`);
|
|
90
99
|
}
|
|
91
100
|
}
|
|
92
101
|
|
|
@@ -95,4 +104,4 @@ if (require.main === module) {
|
|
|
95
104
|
try { run(); } catch { /* graceful failure — don't block session start */ }
|
|
96
105
|
}
|
|
97
106
|
|
|
98
|
-
module.exports = { isNewer, fetchLatestVersion, doAutoUpdate, run };
|
|
107
|
+
module.exports = { isNewer, fetchLatestVersion, doAutoUpdate, run, dateStamp };
|