@tekyzinc/gsd-t 3.10.15 → 3.10.16
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 +15 -0
- package/bin/gsd-t-unattended-platform.cjs +5 -5
- package/bin/gsd-t-unattended-platform.js +5 -5
- package/bin/gsd-t-unattended-safety.cjs +22 -0
- package/bin/gsd-t-unattended-safety.js +22 -0
- package/bin/gsd-t-unattended.cjs +19 -1
- package/bin/gsd-t-unattended.js +19 -1
- package/bin/gsd-t.js +52 -1
- package/commands/gsd-t-unattended.md +33 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to GSD-T are documented here. Updated with each release.
|
|
4
4
|
|
|
5
|
+
## [3.10.16] - 2026-04-15
|
|
6
|
+
|
|
7
|
+
### Fixed — unattended supervisor launch friction (3 bugs + UX improvements)
|
|
8
|
+
|
|
9
|
+
**Background**: Users consistently failed to launch unattended sessions due to compounding pre-flight friction: the supervisor spawn targeted the wrong binary, the dirty-tree check refused on benign files, and missing milestone state caused hard refusals. These issues defeated the purpose of "unattended" mode.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
- **`bin/gsd-t-unattended-platform.{js,cjs}`** — `spawnSupervisor()` no longer prepends `"unattended"` as a subcommand. `binPath` now points to `bin/gsd-t-unattended.cjs` (the actual supervisor entry) instead of `bin/gsd-t.js` (which has no `unattended` subcommand and printed "Unknown command" on every launch).
|
|
13
|
+
- **`bin/gsd-t-unattended.{js,cjs}`** — dirty worktree check changed from **refuse** to **auto-whitelist**. Non-whitelisted dirty files are automatically added to `.gsd-t/.unattended/config.json` and the supervisor proceeds. Only genuine git errors (not a repo, etc.) still refuse. Import of `saveConfig` added.
|
|
14
|
+
- **`bin/gsd-t-unattended-safety.{js,cjs}`** — added `saveConfig(projectDir, config)` function to persist auto-whitelisted entries back to the config file. Exported for use by supervisor and tests.
|
|
15
|
+
- **`bin/gsd-t.js`** `updateSingleProject()` — now calls `ensureUnattendedConfig()` (creates `.gsd-t/.unattended/config.json` with all defaults) and `ensureUnattendedGitignore()` (adds `bin/*.cjs`, `.gsd-t/.archive-migration-v1`, `.gsd-t/.task-counter-retired-v1` to `.gitignore`).
|
|
16
|
+
- **`commands/gsd-t-unattended.md`** — Step 1c.1 "Readiness Bootstrap": if no active milestone found, auto-bootstraps from conversation context or `--milestone=` flag instead of refusing. Works from any workflow state. Step 2 dry-run display and binPath updated to reference `gsd-t-unattended.cjs`.
|
|
17
|
+
- **`.gsd-t/contracts/unattended-supervisor-contract.md`** — spawn snippet and exit code 8 description updated.
|
|
18
|
+
- **Tests**: `test/unattended-platform.test.js` shim updated (argv[2] not argv[3]). `test/unattended-supervisor.test.js` dirty-tree test now verifies auto-whitelist behavior + git-error refusal. 1136/1136 tests pass.
|
|
19
|
+
|
|
5
20
|
## [3.10.15] - 2026-04-15
|
|
6
21
|
|
|
7
22
|
### Fixed — bin tools not propagated to downstream projects (unattended launch fails)
|
|
@@ -178,8 +178,8 @@ function spawnWorker(projectDir, timeoutMs, opts = {}) {
|
|
|
178
178
|
* state file, and relays `claude -p` workers until the milestone terminates.
|
|
179
179
|
*
|
|
180
180
|
* Spawn recipe:
|
|
181
|
-
* - `node {binPath}
|
|
182
|
-
*
|
|
181
|
+
* - `node {binPath} {...args}` — binPath should be the absolute path to
|
|
182
|
+
* `bin/gsd-t-unattended.cjs` (the supervisor entry point), NOT gsd-t.js.
|
|
183
183
|
* - `detached: true` — the child becomes a process-group leader on POSIX
|
|
184
184
|
* (darwin/linux) so it survives the parent closing its terminal. On win32
|
|
185
185
|
* the equivalent flag produces a separate process tree.
|
|
@@ -194,13 +194,13 @@ function spawnWorker(projectDir, timeoutMs, opts = {}) {
|
|
|
194
194
|
* `docs/unattended-windows-caveats.md` (Task 3).
|
|
195
195
|
*
|
|
196
196
|
* @param {object} params
|
|
197
|
-
* @param {string} params.binPath Absolute path to `bin/gsd-t.
|
|
198
|
-
* @param {string[]} params.args
|
|
197
|
+
* @param {string} params.binPath Absolute path to `bin/gsd-t-unattended.cjs`.
|
|
198
|
+
* @param {string[]} params.args CLI args passed directly to the supervisor.
|
|
199
199
|
* @param {string} params.cwd Project directory (supervisor's cwd).
|
|
200
200
|
* @returns {{ pid: number }} The detached child's PID.
|
|
201
201
|
*/
|
|
202
202
|
function spawnSupervisor({ binPath, args, cwd }) {
|
|
203
|
-
const spawnArgs = [binPath,
|
|
203
|
+
const spawnArgs = [binPath, ...(args || [])];
|
|
204
204
|
const opts = {
|
|
205
205
|
cwd,
|
|
206
206
|
detached: true,
|
|
@@ -178,8 +178,8 @@ function spawnWorker(projectDir, timeoutMs, opts = {}) {
|
|
|
178
178
|
* state file, and relays `claude -p` workers until the milestone terminates.
|
|
179
179
|
*
|
|
180
180
|
* Spawn recipe:
|
|
181
|
-
* - `node {binPath}
|
|
182
|
-
*
|
|
181
|
+
* - `node {binPath} {...args}` — binPath should be the absolute path to
|
|
182
|
+
* `bin/gsd-t-unattended.cjs` (the supervisor entry point), NOT gsd-t.js.
|
|
183
183
|
* - `detached: true` — the child becomes a process-group leader on POSIX
|
|
184
184
|
* (darwin/linux) so it survives the parent closing its terminal. On win32
|
|
185
185
|
* the equivalent flag produces a separate process tree.
|
|
@@ -194,13 +194,13 @@ function spawnWorker(projectDir, timeoutMs, opts = {}) {
|
|
|
194
194
|
* `docs/unattended-windows-caveats.md` (Task 3).
|
|
195
195
|
*
|
|
196
196
|
* @param {object} params
|
|
197
|
-
* @param {string} params.binPath Absolute path to `bin/gsd-t.
|
|
198
|
-
* @param {string[]} params.args
|
|
197
|
+
* @param {string} params.binPath Absolute path to `bin/gsd-t-unattended.cjs`.
|
|
198
|
+
* @param {string[]} params.args CLI args passed directly to the supervisor.
|
|
199
199
|
* @param {string} params.cwd Project directory (supervisor's cwd).
|
|
200
200
|
* @returns {{ pid: number }} The detached child's PID.
|
|
201
201
|
*/
|
|
202
202
|
function spawnSupervisor({ binPath, args, cwd }) {
|
|
203
|
-
const spawnArgs = [binPath,
|
|
203
|
+
const spawnArgs = [binPath, ...(args || [])];
|
|
204
204
|
const opts = {
|
|
205
205
|
cwd,
|
|
206
206
|
detached: true,
|
|
@@ -151,6 +151,27 @@ function loadConfig(projectDir) {
|
|
|
151
151
|
return merged;
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
+
// ── saveConfig ─────────────────────────────────────────────────────────────
|
|
155
|
+
//
|
|
156
|
+
// Persists the given config object back to `.gsd-t/.unattended/config.json`.
|
|
157
|
+
// Creates the directory if missing. Used by auto-whitelist to remember newly
|
|
158
|
+
// whitelisted dirty-tree entries so subsequent launches don't re-warn.
|
|
159
|
+
|
|
160
|
+
function saveConfig(projectDir, config) {
|
|
161
|
+
const configDir = path.join(projectDir, ".gsd-t", ".unattended");
|
|
162
|
+
const configPath = path.join(configDir, "config.json");
|
|
163
|
+
if (!fs.existsSync(configDir)) {
|
|
164
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
165
|
+
}
|
|
166
|
+
const serializable = {};
|
|
167
|
+
for (const key of Object.keys(DEFAULTS)) {
|
|
168
|
+
if (config[key] !== undefined) {
|
|
169
|
+
serializable[key] = config[key];
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
fs.writeFileSync(configPath, JSON.stringify(serializable, null, 2) + "\n");
|
|
173
|
+
}
|
|
174
|
+
|
|
154
175
|
// ── checkGitBranch ──────────────────────────────────────────────────────────
|
|
155
176
|
//
|
|
156
177
|
// Runs `git branch --show-current` in projectDir. An empty result indicates
|
|
@@ -745,6 +766,7 @@ function detectBlockerSentinel(runLogTail) {
|
|
|
745
766
|
module.exports = {
|
|
746
767
|
DEFAULTS,
|
|
747
768
|
loadConfig,
|
|
769
|
+
saveConfig,
|
|
748
770
|
checkGitBranch,
|
|
749
771
|
checkWorktreeCleanliness,
|
|
750
772
|
checkIterationCap,
|
|
@@ -151,6 +151,27 @@ function loadConfig(projectDir) {
|
|
|
151
151
|
return merged;
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
+
// ── saveConfig ─────────────────────────────────────────────────────────────
|
|
155
|
+
//
|
|
156
|
+
// Persists the given config object back to `.gsd-t/.unattended/config.json`.
|
|
157
|
+
// Creates the directory if missing. Used by auto-whitelist to remember newly
|
|
158
|
+
// whitelisted dirty-tree entries so subsequent launches don't re-warn.
|
|
159
|
+
|
|
160
|
+
function saveConfig(projectDir, config) {
|
|
161
|
+
const configDir = path.join(projectDir, ".gsd-t", ".unattended");
|
|
162
|
+
const configPath = path.join(configDir, "config.json");
|
|
163
|
+
if (!fs.existsSync(configDir)) {
|
|
164
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
165
|
+
}
|
|
166
|
+
const serializable = {};
|
|
167
|
+
for (const key of Object.keys(DEFAULTS)) {
|
|
168
|
+
if (config[key] !== undefined) {
|
|
169
|
+
serializable[key] = config[key];
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
fs.writeFileSync(configPath, JSON.stringify(serializable, null, 2) + "\n");
|
|
173
|
+
}
|
|
174
|
+
|
|
154
175
|
// ── checkGitBranch ──────────────────────────────────────────────────────────
|
|
155
176
|
//
|
|
156
177
|
// Runs `git branch --show-current` in projectDir. An empty result indicates
|
|
@@ -745,6 +766,7 @@ function detectBlockerSentinel(runLogTail) {
|
|
|
745
766
|
module.exports = {
|
|
746
767
|
DEFAULTS,
|
|
747
768
|
loadConfig,
|
|
769
|
+
saveConfig,
|
|
748
770
|
checkGitBranch,
|
|
749
771
|
checkWorktreeCleanliness,
|
|
750
772
|
checkIterationCap,
|
package/bin/gsd-t-unattended.cjs
CHANGED
|
@@ -35,6 +35,7 @@ const { mapHeadlessExitCode } = require("./gsd-t.js");
|
|
|
35
35
|
const {
|
|
36
36
|
DEFAULTS: SAFETY_DEFAULTS,
|
|
37
37
|
loadConfig,
|
|
38
|
+
saveConfig,
|
|
38
39
|
checkGitBranch,
|
|
39
40
|
checkWorktreeCleanliness,
|
|
40
41
|
checkIterationCap,
|
|
@@ -493,6 +494,7 @@ function doUnattended(argv, deps) {
|
|
|
493
494
|
releaseSleep: deps._releaseSleep || releaseSleep,
|
|
494
495
|
notify: deps._notify || notify,
|
|
495
496
|
loadConfig: deps._loadConfig || loadConfig,
|
|
497
|
+
saveConfig: deps._saveConfig || saveConfig,
|
|
496
498
|
};
|
|
497
499
|
|
|
498
500
|
// ── Load config (optional .gsd-t/.unattended/config.json) ────────────────
|
|
@@ -549,7 +551,23 @@ function doUnattended(argv, deps) {
|
|
|
549
551
|
};
|
|
550
552
|
}
|
|
551
553
|
const treeRes = fn.checkWorktreeCleanliness(projectDir, config);
|
|
552
|
-
if (!treeRes.ok) {
|
|
554
|
+
if (!treeRes.ok && treeRes.dirtyFiles && treeRes.dirtyFiles.length > 0) {
|
|
555
|
+
// Auto-whitelist: add the dirty files to the config and persist, then
|
|
556
|
+
// proceed instead of refusing. This keeps unattended launch frictionless.
|
|
557
|
+
for (const file of treeRes.dirtyFiles) {
|
|
558
|
+
if (!config.dirtyTreeWhitelist.includes(file)) {
|
|
559
|
+
config.dirtyTreeWhitelist.push(file);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
try {
|
|
563
|
+
fn.saveConfig(projectDir, config);
|
|
564
|
+
} catch (_) { /* best effort — config dir may not exist yet */ }
|
|
565
|
+
// eslint-disable-next-line no-console
|
|
566
|
+
console.error(
|
|
567
|
+
`[gsd-t-unattended] auto-whitelisted ${treeRes.dirtyFiles.length} dirty file(s): ${treeRes.dirtyFiles.slice(0, 5).join(", ")}${treeRes.dirtyFiles.length > 5 ? ", …" : ""}`,
|
|
568
|
+
);
|
|
569
|
+
} else if (!treeRes.ok) {
|
|
570
|
+
// Non-file failure (e.g. git error) — still refuse.
|
|
553
571
|
// eslint-disable-next-line no-console
|
|
554
572
|
console.error(
|
|
555
573
|
`[gsd-t-unattended] preflight-refusal: ${treeRes.reason || "dirty worktree"}`,
|
package/bin/gsd-t-unattended.js
CHANGED
|
@@ -35,6 +35,7 @@ const { mapHeadlessExitCode } = require("./gsd-t.js");
|
|
|
35
35
|
const {
|
|
36
36
|
DEFAULTS: SAFETY_DEFAULTS,
|
|
37
37
|
loadConfig,
|
|
38
|
+
saveConfig,
|
|
38
39
|
checkGitBranch,
|
|
39
40
|
checkWorktreeCleanliness,
|
|
40
41
|
checkIterationCap,
|
|
@@ -493,6 +494,7 @@ function doUnattended(argv, deps) {
|
|
|
493
494
|
releaseSleep: deps._releaseSleep || releaseSleep,
|
|
494
495
|
notify: deps._notify || notify,
|
|
495
496
|
loadConfig: deps._loadConfig || loadConfig,
|
|
497
|
+
saveConfig: deps._saveConfig || saveConfig,
|
|
496
498
|
};
|
|
497
499
|
|
|
498
500
|
// ── Load config (optional .gsd-t/.unattended/config.json) ────────────────
|
|
@@ -549,7 +551,23 @@ function doUnattended(argv, deps) {
|
|
|
549
551
|
};
|
|
550
552
|
}
|
|
551
553
|
const treeRes = fn.checkWorktreeCleanliness(projectDir, config);
|
|
552
|
-
if (!treeRes.ok) {
|
|
554
|
+
if (!treeRes.ok && treeRes.dirtyFiles && treeRes.dirtyFiles.length > 0) {
|
|
555
|
+
// Auto-whitelist: add the dirty files to the config and persist, then
|
|
556
|
+
// proceed instead of refusing. This keeps unattended launch frictionless.
|
|
557
|
+
for (const file of treeRes.dirtyFiles) {
|
|
558
|
+
if (!config.dirtyTreeWhitelist.includes(file)) {
|
|
559
|
+
config.dirtyTreeWhitelist.push(file);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
try {
|
|
563
|
+
fn.saveConfig(projectDir, config);
|
|
564
|
+
} catch (_) { /* best effort — config dir may not exist yet */ }
|
|
565
|
+
// eslint-disable-next-line no-console
|
|
566
|
+
console.error(
|
|
567
|
+
`[gsd-t-unattended] auto-whitelisted ${treeRes.dirtyFiles.length} dirty file(s): ${treeRes.dirtyFiles.slice(0, 5).join(", ")}${treeRes.dirtyFiles.length > 5 ? ", …" : ""}`,
|
|
568
|
+
);
|
|
569
|
+
} else if (!treeRes.ok) {
|
|
570
|
+
// Non-file failure (e.g. git error) — still refuse.
|
|
553
571
|
// eslint-disable-next-line no-console
|
|
554
572
|
console.error(
|
|
555
573
|
`[gsd-t-unattended] preflight-refusal: ${treeRes.reason || "dirty worktree"}`,
|
package/bin/gsd-t.js
CHANGED
|
@@ -1964,7 +1964,9 @@ function updateSingleProject(projectDir, counts) {
|
|
|
1964
1964
|
const binToolsCopied = copyBinToolsToProject(projectDir, projectName);
|
|
1965
1965
|
const archiveRan = runProgressArchiveMigration(projectDir, projectName);
|
|
1966
1966
|
const taskCounterRetired = runTaskCounterRetirementMigration(projectDir, projectName);
|
|
1967
|
-
|
|
1967
|
+
const unattendedConfigCreated = ensureUnattendedConfig(projectDir, projectName);
|
|
1968
|
+
const gitignoreUpdated = ensureUnattendedGitignore(projectDir, projectName);
|
|
1969
|
+
if (guardAdded || changelogCreated || binToolsCopied || archiveRan || taskCounterRetired || unattendedConfigCreated || gitignoreUpdated) {
|
|
1968
1970
|
counts.updated++;
|
|
1969
1971
|
} else {
|
|
1970
1972
|
info(`${projectName} — already up to date`);
|
|
@@ -2104,6 +2106,55 @@ function runTaskCounterRetirementMigration(projectDir, projectName) {
|
|
|
2104
2106
|
return true;
|
|
2105
2107
|
}
|
|
2106
2108
|
|
|
2109
|
+
function ensureUnattendedConfig(projectDir, projectName) {
|
|
2110
|
+
const configDir = path.join(projectDir, ".gsd-t", ".unattended");
|
|
2111
|
+
const configPath = path.join(configDir, "config.json");
|
|
2112
|
+
if (fs.existsSync(configPath)) return false;
|
|
2113
|
+
if (!fs.existsSync(path.join(projectDir, ".gsd-t"))) return false;
|
|
2114
|
+
|
|
2115
|
+
try {
|
|
2116
|
+
if (!fs.existsSync(configDir)) {
|
|
2117
|
+
fs.mkdirSync(configDir, { recursive: true });
|
|
2118
|
+
}
|
|
2119
|
+
const defaultConfig = {
|
|
2120
|
+
protectedBranches: ["main", "master", "develop", "trunk", "release/*", "hotfix/*"],
|
|
2121
|
+
dirtyTreeWhitelist: [
|
|
2122
|
+
".gsd-t/heartbeat-*.jsonl",
|
|
2123
|
+
".gsd-t/.context-meter-state.json",
|
|
2124
|
+
".gsd-t/events/*.jsonl",
|
|
2125
|
+
".gsd-t/token-metrics.jsonl",
|
|
2126
|
+
".gsd-t/token-log.md",
|
|
2127
|
+
".gsd-t/.unattended/*",
|
|
2128
|
+
".gsd-t/.handoff/*",
|
|
2129
|
+
".claude/settings.local.json",
|
|
2130
|
+
".claude/settings.local.json.bak*",
|
|
2131
|
+
],
|
|
2132
|
+
maxIterations: 200,
|
|
2133
|
+
hours: 24,
|
|
2134
|
+
};
|
|
2135
|
+
fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2) + "\n");
|
|
2136
|
+
info(`${projectName} — created .gsd-t/.unattended/config.json (edit to customize)`);
|
|
2137
|
+
return true;
|
|
2138
|
+
} catch (e) {
|
|
2139
|
+
warn(`${projectName} — failed to create unattended config: ${e.message}`);
|
|
2140
|
+
return false;
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
const UNATTENDED_GITIGNORE_ENTRIES = [
|
|
2145
|
+
"bin/*.cjs",
|
|
2146
|
+
".gsd-t/.archive-migration-v1",
|
|
2147
|
+
".gsd-t/.task-counter-retired-v1",
|
|
2148
|
+
];
|
|
2149
|
+
|
|
2150
|
+
function ensureUnattendedGitignore(projectDir, projectName) {
|
|
2151
|
+
const added = ensureGitignoreEntries(projectDir, UNATTENDED_GITIGNORE_ENTRIES);
|
|
2152
|
+
if (added) {
|
|
2153
|
+
info(`${projectName} — added GSD-T entries to .gitignore`);
|
|
2154
|
+
}
|
|
2155
|
+
return added;
|
|
2156
|
+
}
|
|
2157
|
+
|
|
2107
2158
|
function showUpdateAllSummary(total, counts, playwrightMissing, swaggerMissing, syncCount) {
|
|
2108
2159
|
log("");
|
|
2109
2160
|
heading("Update All Complete");
|
|
@@ -112,6 +112,37 @@ try {
|
|
|
112
112
|
"
|
|
113
113
|
```
|
|
114
114
|
|
|
115
|
+
### 1c.1: Readiness Bootstrap (auto-complete pending state)
|
|
116
|
+
|
|
117
|
+
**The unattended command must work from ANY workflow state.** If the user says "run unattended," that is an instruction to proceed — not an invitation to ask more questions. Regardless of where the project is in the GSD-T workflow (mid-discussion, pre-milestone, post-partition, between phases), the unattended supervisor should be able to pick up and continue.
|
|
118
|
+
|
|
119
|
+
The `/gsd-t-resume` worker already knows how to chain through phases automatically. The only thing the unattended command needs is a milestone label in `progress.md` so the supervisor has a target.
|
|
120
|
+
|
|
121
|
+
**If `MILESTONE=unknown`** (no active milestone found in progress.md):
|
|
122
|
+
|
|
123
|
+
1. **Look for a milestone** — check in priority order:
|
|
124
|
+
a. `$ARGUMENTS` — if `--milestone=LABEL` was passed, use it.
|
|
125
|
+
b. Conversation context — if the user discussed a milestone, feature, or work goal in this session, extract the milestone label from that discussion.
|
|
126
|
+
c. `.gsd-t/progress.md` — check for any milestone mentioned anywhere (even if not in the standard "Current Milestone" format, e.g., a Decision Log entry like "created milestone M8").
|
|
127
|
+
d. `.gsd-t/domains/` — if domains exist, infer the milestone from directory names or scope files.
|
|
128
|
+
|
|
129
|
+
2. **If a milestone is identifiable**:
|
|
130
|
+
- Ensure `.gsd-t/progress.md` has a `## Current Milestone` section with the label and status `IN PROGRESS`. If missing, add it.
|
|
131
|
+
- Run `gsd-t-init` silently to fill in any missing required files (it skips existing files).
|
|
132
|
+
- Print: `ℹ️ Auto-bootstrapped milestone {LABEL} — supervisor workers will continue from current phase.`
|
|
133
|
+
- Re-read the milestone label.
|
|
134
|
+
|
|
135
|
+
3. **If no milestone is identifiable at all**:
|
|
136
|
+
- Print:
|
|
137
|
+
```
|
|
138
|
+
❌ No active milestone found and none identifiable from context.
|
|
139
|
+
|
|
140
|
+
Specify one explicitly: /user:gsd-t-unattended --milestone=M1
|
|
141
|
+
```
|
|
142
|
+
- **STOP.** Do NOT spawn.
|
|
143
|
+
|
|
144
|
+
**If `MILESTONE` was found but progress.md shows a pre-execution phase** (e.g., milestone was just created, no partition/plan/domains yet): that's fine — proceed with the spawn. The `/gsd-t-resume` worker will read the current state and advance through partition → plan → execute → ... automatically. The unattended command should never second-guess the resume logic or add pre-conditions beyond "a milestone exists."
|
|
145
|
+
|
|
115
146
|
### 1d: Check for Stale Stop Sentinel
|
|
116
147
|
|
|
117
148
|
If `.gsd-t/.unattended/stop` exists from a previous run, remove it before spawning (per contract §10 — the launch command cleans the stale sentinel):
|
|
@@ -230,7 +261,7 @@ If `--dry-run` was specified, print:
|
|
|
230
261
|
|
|
231
262
|
```
|
|
232
263
|
🔎 Dry-run mode — would spawn:
|
|
233
|
-
node bin/gsd-t.
|
|
264
|
+
node bin/gsd-t-unattended.cjs --hours={hours} --milestone={milestone} --max-iterations={maxIterations}
|
|
234
265
|
Project: {cwd}
|
|
235
266
|
|
|
236
267
|
No supervisor launched. Remove --dry-run to proceed.
|
|
@@ -250,7 +281,7 @@ const hours = parseInt(process.env.GSD_T_HOURS || '24', 10) || 24;
|
|
|
250
281
|
const milestone = process.env.GSD_T_MILESTONE || '';
|
|
251
282
|
const maxIterations = parseInt(process.env.GSD_T_MAX_ITERATIONS || '200', 10) || 200;
|
|
252
283
|
|
|
253
|
-
const binPath = path.resolve(__dirname, 'bin', 'gsd-t.
|
|
284
|
+
const binPath = path.resolve(__dirname, 'bin', 'gsd-t-unattended.cjs');
|
|
254
285
|
const cwd = process.cwd();
|
|
255
286
|
|
|
256
287
|
const extraArgs = [];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tekyzinc/gsd-t",
|
|
3
|
-
"version": "3.10.
|
|
3
|
+
"version": "3.10.16",
|
|
4
4
|
"description": "GSD-T: Contract-Driven Development for Claude Code — 61 slash commands with unattended supervisor relay, headless CI/CD mode, graph-powered code analysis, real-time agent dashboard, execution intelligence, 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",
|