@tekyzinc/gsd-t 3.10.15 → 3.11.10
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 +28 -0
- package/README.md +1 -1
- package/bin/gsd-t-unattended-platform.cjs +0 -0
- package/bin/gsd-t-unattended-platform.js +5 -5
- package/bin/gsd-t-unattended-safety.cjs +0 -0
- package/bin/gsd-t-unattended-safety.js +22 -0
- package/bin/gsd-t-unattended.cjs +7 -1
- package/bin/gsd-t-unattended.js +19 -1
- package/bin/gsd-t.js +52 -1
- package/commands/gsd-t-debug.md +10 -0
- package/commands/gsd-t-execute.md +10 -0
- package/commands/gsd-t-integrate.md +10 -0
- package/commands/gsd-t-quick.md +10 -0
- package/commands/gsd-t-resume.md +3 -1
- package/commands/gsd-t-unattended-stop.md +5 -3
- package/commands/gsd-t-unattended-watch.md +87 -13
- package/commands/gsd-t-unattended.md +35 -4
- package/commands/gsd-t-wave.md +10 -0
- package/docs/architecture.md +1 -1
- package/docs/requirements.md +2 -2
- package/package.json +1 -1
- package/scripts/context-meter/threshold.js +11 -3
- package/scripts/context-meter/threshold.test.js +22 -15
- package/scripts/gsd-t-agent-dashboard-server.js +424 -0
- package/scripts/gsd-t-agent-dashboard.html +724 -0
- package/scripts/gsd-t-context-meter.e2e.test.js +6 -4
- package/templates/CLAUDE-global.md +19 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,34 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to GSD-T are documented here. Updated with each release.
|
|
4
4
|
|
|
5
|
+
## [3.11.10] - 2026-04-16
|
|
6
|
+
|
|
7
|
+
### Added — Universal Context Auto-Pause (M37)
|
|
8
|
+
|
|
9
|
+
**Background**: The Context Meter (M34) correctly measures context window usage and emits an `additionalContext` signal at the configured threshold (default 75%). However, Claude consistently ignores the single-line suggestion format, continuing to work until hitting the runtime's ~95% `/compact` wall — which destroys context silently and loses work.
|
|
10
|
+
|
|
11
|
+
### Changed
|
|
12
|
+
- **`scripts/context-meter/threshold.js`** — `buildAdditionalContext()` now returns a 6-line MANDATORY STOP instruction instead of a single-line suggestion. The message starts with `🛑 MANDATORY STOP` and includes step-by-step instructions (pause → clear → resume) with explicit reference to the Destructive Action Guard enforcement weight.
|
|
13
|
+
- **`.gsd-t/contracts/context-meter-contract.md`** — bumped to v1.2.0. New §"Universal Auto-Pause Rule" documents the mandatory behavioral requirement. Rule #8 added: `additionalContext` is a MANDATORY STOP signal with Destructive Action Guard enforcement weight.
|
|
14
|
+
- **`templates/CLAUDE-global.md`** — new `## Universal Auto-Pause Rule (MANDATORY)` section added between Context Meter and API Documentation Guard sections. Same enforcement weight as the Destructive Action Guard.
|
|
15
|
+
- **5 loop command files** (`gsd-t-execute`, `gsd-t-wave`, `gsd-t-integrate`, `gsd-t-quick`, `gsd-t-debug`) — Step 0.2 added: Universal Auto-Pause Rule enforcement. If `🛑 MANDATORY STOP` appears in `additionalContext` at any point, immediately halt, pause, and instruct clear+resume.
|
|
16
|
+
- **Tests**: All 1228 tests pass (1224 unit + 4 e2e). `threshold.test.js` and `gsd-t-context-meter.e2e.test.js` updated for new multi-line format.
|
|
17
|
+
|
|
18
|
+
## [3.10.16] - 2026-04-15
|
|
19
|
+
|
|
20
|
+
### Fixed — unattended supervisor launch friction (3 bugs + UX improvements)
|
|
21
|
+
|
|
22
|
+
**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.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
- **`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).
|
|
26
|
+
- **`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.
|
|
27
|
+
- **`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.
|
|
28
|
+
- **`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`).
|
|
29
|
+
- **`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`.
|
|
30
|
+
- **`.gsd-t/contracts/unattended-supervisor-contract.md`** — spawn snippet and exit code 8 description updated.
|
|
31
|
+
- **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.
|
|
32
|
+
|
|
5
33
|
## [3.10.15] - 2026-04-15
|
|
6
34
|
|
|
7
35
|
### Fixed — bin tools not propagated to downstream projects (unattended launch fails)
|
package/README.md
CHANGED
|
@@ -338,7 +338,7 @@ gsd-t unattended --hours=24
|
|
|
338
338
|
**How it works:**
|
|
339
339
|
|
|
340
340
|
- `gsd-t unattended` spawns `bin/gsd-t-unattended.js` as a fully detached OS process. The supervisor runs `claude -p` workers in a relay — one worker per iteration — each in a fresh context window. State is written atomically to `.gsd-t/.unattended/state.json` between iterations.
|
|
341
|
-
- `/user:gsd-t-unattended` does the same from inside Claude Code, then calls `ScheduleWakeup(270, '/
|
|
341
|
+
- `/user:gsd-t-unattended` does the same from inside Claude Code, then calls `ScheduleWakeup(270, '/gsd-t-unattended-watch')` to start an in-session watch loop that ticks every 270 seconds and prints progress.
|
|
342
342
|
- If you run `/clear` + `/user:gsd-t-resume` during a live run, the resume command auto-detects the running supervisor and re-attaches the watch loop — no re-launch needed.
|
|
343
343
|
- The supervisor halts automatically when: the milestone reaches COMPLETED status, the `--hours` wall-clock cap expires, `--max-iterations` is reached, safety rails detect a stall or unrecoverable error, or the stop sentinel is touched.
|
|
344
344
|
|
|
File without changes
|
|
@@ -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,
|
|
File without changes
|
|
@@ -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
|
@@ -987,9 +987,15 @@ function runMainLoop(state, dir, opts, deps, ctx) {
|
|
|
987
987
|
*/
|
|
988
988
|
function _spawnWorker(state, opts) {
|
|
989
989
|
const bin = (state && state.claudeBin) || resolveClaudePath();
|
|
990
|
+
const workerEnv = { ...process.env, GSD_T_UNATTENDED_WORKER: "1" };
|
|
990
991
|
const res = platformSpawnWorker(opts.cwd, opts.timeout, {
|
|
991
992
|
bin,
|
|
992
|
-
args: [
|
|
993
|
+
args: [
|
|
994
|
+
"-p",
|
|
995
|
+
"You are an unattended worker iteration. CRITICAL: Do NOT check supervisor.pid, do NOT auto-reattach to a watch loop, do NOT schedule any ScheduleWakeup. You ARE the worker spawned by the supervisor. Skip Step 0 (auto-reattach) entirely and go directly to Step 0.1. Run /gsd-t-resume but skip the unattended supervisor auto-reattach check in Step 0.",
|
|
996
|
+
"--dangerously-skip-permissions",
|
|
997
|
+
],
|
|
998
|
+
env: workerEnv,
|
|
993
999
|
});
|
|
994
1000
|
return {
|
|
995
1001
|
status: typeof res.status === "number" ? res.status : null,
|
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");
|
package/commands/gsd-t-debug.md
CHANGED
|
@@ -65,6 +65,16 @@ If `can_start === false`, the headless continuation has been spawned and the int
|
|
|
65
65
|
|
|
66
66
|
**Contract**: `.gsd-t/contracts/runway-estimator-contract.md` v1.0.0; stop threshold (85%) mirrors `.gsd-t/contracts/token-budget-contract.md` v3.0.0.
|
|
67
67
|
|
|
68
|
+
## Step 0.2: Universal Auto-Pause Rule (MANDATORY — context-meter-contract v1.2.0, M37)
|
|
69
|
+
|
|
70
|
+
**If at ANY point during this command you see a `🛑 MANDATORY STOP` message in `additionalContext` (delivered by the Context Meter PostToolUse hook), you MUST:**
|
|
71
|
+
1. Immediately stop all work — do NOT continue debugging, do NOT spawn the next subagent
|
|
72
|
+
2. Run `/user:gsd-t-pause` to save exact position
|
|
73
|
+
3. Tell the user to run `/clear` then `/user:gsd-t-resume`
|
|
74
|
+
4. STOP — do not continue
|
|
75
|
+
|
|
76
|
+
This has the same enforcement weight as the Destructive Action Guard. The signal means the context window is at or above the configured threshold (default 75%) and continuing risks hitting the runtime's ~95% `/compact` wall.
|
|
77
|
+
|
|
68
78
|
## Step 0.1: Launch via Subagent
|
|
69
79
|
|
|
70
80
|
To give this debug session a fresh context window and prevent compaction, always execute via a Task subagent.
|
|
@@ -83,6 +83,16 @@ Use the returned `threshold` as the gate signal for the rest of this run. The ga
|
|
|
83
83
|
|
|
84
84
|
Why: every `/user:gsd-t-execute` invocation is a fresh orchestrator session and needs a current reading of context utilization before spawning any subagents. The authoritative source is the Context Meter state file; the fallback keeps the gate functional on projects that haven't installed the hook yet.
|
|
85
85
|
|
|
86
|
+
## Step 0.2: Universal Auto-Pause Rule (MANDATORY — context-meter-contract v1.2.0, M37)
|
|
87
|
+
|
|
88
|
+
**If at ANY point during this command you see a `🛑 MANDATORY STOP` message in `additionalContext` (delivered by the Context Meter PostToolUse hook), you MUST:**
|
|
89
|
+
1. Immediately stop all work — do NOT finish the current task, do NOT spawn the next subagent
|
|
90
|
+
2. Run `/user:gsd-t-pause` to save exact position
|
|
91
|
+
3. Tell the user to run `/clear` then `/user:gsd-t-resume`
|
|
92
|
+
4. STOP — do not continue
|
|
93
|
+
|
|
94
|
+
This has the same enforcement weight as the Destructive Action Guard. The signal means the context window is at or above the configured threshold (default 75%) and continuing risks hitting the runtime's ~95% `/compact` wall.
|
|
95
|
+
|
|
86
96
|
## Step 1: Load State
|
|
87
97
|
|
|
88
98
|
Read:
|
|
@@ -65,6 +65,16 @@ If `can_start === false`, the headless continuation has been spawned and the int
|
|
|
65
65
|
|
|
66
66
|
**Contract**: `.gsd-t/contracts/runway-estimator-contract.md` v1.0.0; stop threshold (85%) mirrors `.gsd-t/contracts/token-budget-contract.md` v3.0.0.
|
|
67
67
|
|
|
68
|
+
## Step 0.2: Universal Auto-Pause Rule (MANDATORY — context-meter-contract v1.2.0, M37)
|
|
69
|
+
|
|
70
|
+
**If at ANY point during this command you see a `🛑 MANDATORY STOP` message in `additionalContext` (delivered by the Context Meter PostToolUse hook), you MUST:**
|
|
71
|
+
1. Immediately stop all work — do NOT continue integration, do NOT spawn the next subagent
|
|
72
|
+
2. Run `/user:gsd-t-pause` to save exact position
|
|
73
|
+
3. Tell the user to run `/clear` then `/user:gsd-t-resume`
|
|
74
|
+
4. STOP — do not continue
|
|
75
|
+
|
|
76
|
+
This has the same enforcement weight as the Destructive Action Guard. The signal means the context window is at or above the configured threshold (default 75%) and continuing risks hitting the runtime's ~95% `/compact` wall.
|
|
77
|
+
|
|
68
78
|
## Step 1: Load Full State
|
|
69
79
|
|
|
70
80
|
Read everything:
|
package/commands/gsd-t-quick.md
CHANGED
|
@@ -65,6 +65,16 @@ If `can_start === false`, the headless continuation has been spawned and the int
|
|
|
65
65
|
|
|
66
66
|
**Contract**: `.gsd-t/contracts/runway-estimator-contract.md` v1.0.0; stop threshold (85%) mirrors `.gsd-t/contracts/token-budget-contract.md` v3.0.0.
|
|
67
67
|
|
|
68
|
+
## Step 0.2: Universal Auto-Pause Rule (MANDATORY — context-meter-contract v1.2.0, M37)
|
|
69
|
+
|
|
70
|
+
**If at ANY point during this command you see a `🛑 MANDATORY STOP` message in `additionalContext` (delivered by the Context Meter PostToolUse hook), you MUST:**
|
|
71
|
+
1. Immediately stop all work — do NOT continue the task, do NOT spawn the next subagent
|
|
72
|
+
2. Run `/user:gsd-t-pause` to save exact position
|
|
73
|
+
3. Tell the user to run `/clear` then `/user:gsd-t-resume`
|
|
74
|
+
4. STOP — do not continue
|
|
75
|
+
|
|
76
|
+
This has the same enforcement weight as the Destructive Action Guard. The signal means the context window is at or above the configured threshold (default 75%) and continuing risks hitting the runtime's ~95% `/compact` wall.
|
|
77
|
+
|
|
68
78
|
## Step 0.1: Launch via Subagent
|
|
69
79
|
|
|
70
80
|
To give this task a fresh context window and prevent compaction during consecutive quick runs, always execute via a Task subagent.
|
package/commands/gsd-t-resume.md
CHANGED
|
@@ -6,6 +6,8 @@ You are resuming work after an interruption. This handles both same-session paus
|
|
|
6
6
|
|
|
7
7
|
**This step runs FIRST, before reading any docs, contracts, or continue-here files.**
|
|
8
8
|
|
|
9
|
+
**Worker bypass**: If the environment variable `GSD_T_UNATTENDED_WORKER=1` is set, this resume is being invoked by the unattended supervisor as a worker iteration. **SKIP this entire Step 0** — do NOT check for supervisor.pid, do NOT auto-reattach, do NOT schedule a watch tick. Fall through directly to Step 0.1. The worker's job is to do actual work, not watch itself.
|
|
10
|
+
|
|
9
11
|
Check whether an unattended supervisor is actively running for this project:
|
|
10
12
|
|
|
11
13
|
1. Check if `.gsd-t/.unattended/supervisor.pid` exists.
|
|
@@ -22,7 +24,7 @@ Check whether an unattended supervisor is actively running for this project:
|
|
|
22
24
|
- **Terminal status** (`done`, `failed`, `stopped`, `crashed`) → the supervisor has finished and is waiting for cleanup. Fall through to Step 0.1 so normal resume flow runs (it will see progress.md state and continue from where the supervisor left off).
|
|
23
25
|
- **Non-terminal status** (`initializing`, `running`, or any unrecognized value) → **AUTO-REATTACH**:
|
|
24
26
|
- Print the current watch status using the data in `state.json` (elapsed time, current iteration, milestone/wave/task, last worker exit code).
|
|
25
|
-
- Call `ScheduleWakeup(270, '/
|
|
27
|
+
- Call `ScheduleWakeup(270, '/gsd-t-unattended-watch', reason='resumed watch')`.
|
|
26
28
|
- **STOP reading resume.md entirely. Do NOT proceed to Step 0.1 or any later step. Do NOT read docs, contracts, or continue-here files. Do NOT display a headless read-back banner.** The watcher will display the live status block and re-schedule itself. Return now.
|
|
27
29
|
|
|
28
30
|
Contract reference: `unattended-supervisor-contract.md` §9 (Resume Auto-Reattach Handshake)
|
|
@@ -66,14 +66,16 @@ This is race-free, terminal-close-safe, and language-agnostic (per contract §10
|
|
|
66
66
|
Output the confirmation block:
|
|
67
67
|
|
|
68
68
|
```
|
|
69
|
-
🛑 Stop sentinel written. Supervisor will halt
|
|
69
|
+
🛑 Stop sentinel written. Supervisor will halt after the current worker finishes (up to ~5 min).
|
|
70
70
|
|
|
71
71
|
Session: {SESSION from Step 3}
|
|
72
72
|
Iter: {ITER from Step 3}
|
|
73
73
|
Status: {STATUS from Step 3}
|
|
74
74
|
|
|
75
|
-
The current worker
|
|
76
|
-
|
|
75
|
+
The current worker runs to completion. Stop is honored at the next pre-worker checkpoint.
|
|
76
|
+
The watch loop (if running) will detect status=stopped on its next tick and stop itself.
|
|
77
|
+
If a watch tick fires before the supervisor processes the sentinel, it may reschedule once
|
|
78
|
+
more — this is expected. It will catch the terminal status on the following tick.
|
|
77
79
|
```
|
|
78
80
|
|
|
79
81
|
## Step 6: Return Immediately
|
|
@@ -20,7 +20,7 @@ See `unattended-supervisor-contract.md` §8 (Watch Tick Decision Tree), §4 (Sta
|
|
|
20
20
|
- failed → failure summary, STOP
|
|
21
21
|
- stopped → user-stop confirm, STOP
|
|
22
22
|
- initializing | running → render watch block, go to 4
|
|
23
|
-
4. ScheduleWakeup(270, '/
|
|
23
|
+
4. ScheduleWakeup(270, '/gsd-t-unattended-watch', reason='unattended tick {iter}')
|
|
24
24
|
```
|
|
25
25
|
|
|
26
26
|
**Critical**: Every branch except `initializing`/`running` is TERMINAL — do NOT reschedule. When you reach a terminal state, print the report and end the turn. Do not add a "Next Up" block — this is a self-rescheduling loop, not a phase workflow.
|
|
@@ -126,6 +126,72 @@ const last50 = tailLines.slice(-50);
|
|
|
126
126
|
const last1 = tailLines.length > 0 ? tailLines[tailLines.length - 1] : '';
|
|
127
127
|
out('LOG_TAIL_LAST1', last1);
|
|
128
128
|
out('LOG_TAIL_LAST50', last50.join('\n'));
|
|
129
|
+
|
|
130
|
+
// --- workflow progress from progress.md and domain task files ---
|
|
131
|
+
let phase = 'unknown';
|
|
132
|
+
let waveInfo = '';
|
|
133
|
+
let waveCurrent = 0;
|
|
134
|
+
let waveTotal = 0;
|
|
135
|
+
let tasksDone = 0;
|
|
136
|
+
let tasksTotal = 0;
|
|
137
|
+
let domainSummary = [];
|
|
138
|
+
try {
|
|
139
|
+
const prog = fs.readFileSync('.gsd-t/progress.md', 'utf8');
|
|
140
|
+
// Extract phase from Status line
|
|
141
|
+
const statusM = prog.match(/^## Status:.*?(\b(?:DEFINED|PARTITIONED|PLANNED|EXECUTING|EXECUTED|INTEGRATING|VERIFYING|VERIFIED|COMPLETE|IN.PROGRESS)\b)/mi);
|
|
142
|
+
if (statusM) phase = statusM[1].trim();
|
|
143
|
+
// Extract "Wave N of M" or latest "Wave N" from Decision Log
|
|
144
|
+
const waveOfM = prog.match(/Wave\s+(\d+)\s+(?:of|\/)\s+(\d+)/gi);
|
|
145
|
+
if (waveOfM && waveOfM.length > 0) {
|
|
146
|
+
const last = waveOfM[waveOfM.length - 1];
|
|
147
|
+
const parts = last.match(/(\d+)\s+(?:of|\/)\s+(\d+)/);
|
|
148
|
+
if (parts) { waveCurrent = parseInt(parts[1], 10); waveTotal = parseInt(parts[2], 10); }
|
|
149
|
+
} else {
|
|
150
|
+
const waveM = prog.match(/Wave\s+(\d+)/gi);
|
|
151
|
+
if (waveM && waveM.length > 0) {
|
|
152
|
+
const last = waveM[waveM.length - 1].match(/(\d+)/);
|
|
153
|
+
if (last) waveCurrent = parseInt(last[1], 10);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
} catch (_) {}
|
|
157
|
+
|
|
158
|
+
// Read domain task files for completion counts and wave totals
|
|
159
|
+
try {
|
|
160
|
+
const domainsDir = '.gsd-t/domains';
|
|
161
|
+
if (fs.existsSync(domainsDir)) {
|
|
162
|
+
const domains = fs.readdirSync(domainsDir).filter(d => {
|
|
163
|
+
try { return fs.statSync(path.join(domainsDir, d)).isDirectory(); } catch (_) { return false; }
|
|
164
|
+
});
|
|
165
|
+
let maxWave = 0;
|
|
166
|
+
for (const d of domains) {
|
|
167
|
+
const tasksFile = path.join(domainsDir, d, 'tasks.md');
|
|
168
|
+
if (!fs.existsSync(tasksFile)) continue;
|
|
169
|
+
const txt = fs.readFileSync(tasksFile, 'utf8');
|
|
170
|
+
const lines = txt.split('\n');
|
|
171
|
+
let done = 0, total = 0;
|
|
172
|
+
for (const line of lines) {
|
|
173
|
+
// Count wave headers to determine total waves
|
|
174
|
+
const wH = line.match(/^#+\s*Wave\s+(\d+)/i);
|
|
175
|
+
if (wH) { const w = parseInt(wH[1], 10); if (w > maxWave) maxWave = w; }
|
|
176
|
+
if (/^[-*]\s*\[x\]/i.test(line)) { done++; total++; }
|
|
177
|
+
else if (/^[-*]\s*\[\s?\]/.test(line)) { total++; }
|
|
178
|
+
}
|
|
179
|
+
if (total > 0) {
|
|
180
|
+
tasksDone += done;
|
|
181
|
+
tasksTotal += total;
|
|
182
|
+
domainSummary.push(d + ':' + done + '/' + total);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (maxWave > 0 && waveTotal === 0) waveTotal = maxWave;
|
|
186
|
+
}
|
|
187
|
+
} catch (_) {}
|
|
188
|
+
if (waveCurrent > 0 && waveTotal > 0) waveInfo = 'Wave ' + waveCurrent + ' of ' + waveTotal;
|
|
189
|
+
else if (waveCurrent > 0) waveInfo = 'Wave ' + waveCurrent;
|
|
190
|
+
out('PHASE', phase);
|
|
191
|
+
out('WAVE_INFO', waveInfo);
|
|
192
|
+
out('TASKS_DONE', tasksDone);
|
|
193
|
+
out('TASKS_TOTAL', tasksTotal);
|
|
194
|
+
out('DOMAIN_SUMMARY', domainSummary.join(' | '));
|
|
129
195
|
"
|
|
130
196
|
```
|
|
131
197
|
|
|
@@ -239,23 +305,31 @@ The supervisor is still alive but has transitioned to a terminal status on its l
|
|
|
239
305
|
|
|
240
306
|
If `PID_FILE_EXISTS=true` AND `ALIVE=true` AND `STATUS` is `initializing` or `running`:
|
|
241
307
|
|
|
242
|
-
Render the
|
|
308
|
+
Render the enriched watch block below. The key insight: users need **workflow progress** (phase, wave, tasks done/remaining), not just supervisor mechanics (iter, PID, exit code).
|
|
309
|
+
|
|
310
|
+
Format rules:
|
|
243
311
|
- One extra space after each emoji (per CLAUDE.md markdown table rules — preserves alignment in terminal views).
|
|
244
312
|
- Elapsed formatted as `{H}h{M}m` from `ELAPSED_MS`.
|
|
245
|
-
- Last-tick age formatted as `{S}s` or `{M}m{S}s`. If age > 540s (2× tick cadence), append ` ⚠️ stale` as a soft warning
|
|
246
|
-
-
|
|
247
|
-
-
|
|
248
|
-
- Last non-empty `run.log` line is truncated to 80 chars.
|
|
313
|
+
- Last-tick age formatted as `{S}s` or `{M}m{S}s`. If age > 540s (2× tick cadence), append ` ⚠️ stale` as a soft warning.
|
|
314
|
+
- Tasks progress bar: `[████████░░░░] 8/12` — filled blocks proportional to done/total.
|
|
315
|
+
- Domain breakdown only shown if ≤6 domains (otherwise too noisy).
|
|
249
316
|
|
|
250
317
|
```
|
|
251
|
-
⚙ Unattended — {MILESTONE}
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
318
|
+
⚙ Unattended — {MILESTONE} · {PHASE}{ · {WAVE_INFO}}
|
|
319
|
+
📋 Tasks: {TASKS_DONE}/{TASKS_TOTAL} [{progress bar}] · elapsed {Hh Mm}
|
|
320
|
+
🔧 {DOMAIN_SUMMARY or "No domains yet (pre-partition)"}
|
|
321
|
+
⏱ Iter {ITER}/{MAX_ITER} · last tick {tickAge} · last exit {LAST_EXIT} ({durationSec}s)
|
|
255
322
|
⏰ Next tick in 270s · Stop: /user:gsd-t-unattended-stop
|
|
256
323
|
```
|
|
257
324
|
|
|
258
|
-
|
|
325
|
+
Progress bar rendering (8 chars wide):
|
|
326
|
+
- If `TASKS_TOTAL > 0`: filled = round(TASKS_DONE / TASKS_TOTAL * 8), empty = 8 - filled. Use `█` for filled, `░` for empty.
|
|
327
|
+
- If `TASKS_TOTAL == 0`: show `[░░░░░░░░]` (pre-partition, no tasks yet).
|
|
328
|
+
|
|
329
|
+
Domain summary formatting:
|
|
330
|
+
- Each domain: `{name}: {done}/{total}` separated by ` | `
|
|
331
|
+
- If domains > 6, show top 3 incomplete + "... +{N} more"
|
|
332
|
+
- If no domains exist, show phase-appropriate message: "Partitioning..." / "Planning..." / "No domains yet"
|
|
259
333
|
|
|
260
334
|
## Step 7: Reschedule via ScheduleWakeup (Non-Terminal Only)
|
|
261
335
|
|
|
@@ -264,7 +338,7 @@ Render the compact watch block below. Format rules:
|
|
|
264
338
|
Call the harness `ScheduleWakeup` tool with these exact parameters:
|
|
265
339
|
|
|
266
340
|
- `delaySeconds`: `270` (fixed — inside the 5-minute prompt-cache TTL)
|
|
267
|
-
- `prompt`: `/
|
|
341
|
+
- `prompt`: `/gsd-t-unattended-watch`
|
|
268
342
|
- `reason`: `unattended tick {ITER}` — substituting the integer `ITER` from Step 2
|
|
269
343
|
|
|
270
344
|
Tool invocation pattern (make this real tool call, not a bash command):
|
|
@@ -272,7 +346,7 @@ Tool invocation pattern (make this real tool call, not a bash command):
|
|
|
272
346
|
```
|
|
273
347
|
ScheduleWakeup(
|
|
274
348
|
delaySeconds: 270,
|
|
275
|
-
prompt: "/
|
|
349
|
+
prompt: "/gsd-t-unattended-watch",
|
|
276
350
|
reason: "unattended tick {ITER}"
|
|
277
351
|
)
|
|
278
352
|
```
|
|
@@ -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 = [];
|
|
@@ -393,7 +424,7 @@ Print the launch confirmation block:
|
|
|
393
424
|
Call the harness `ScheduleWakeup` tool with these exact parameters:
|
|
394
425
|
|
|
395
426
|
- `delaySeconds`: `270` (fixed — inside the 5-minute prompt-cache TTL)
|
|
396
|
-
- `prompt`: `/
|
|
427
|
+
- `prompt`: `/gsd-t-unattended-watch`
|
|
397
428
|
- `reason`: `first unattended tick`
|
|
398
429
|
|
|
399
430
|
Tool invocation pattern (make this a real tool call, not a bash command):
|
|
@@ -401,7 +432,7 @@ Tool invocation pattern (make this a real tool call, not a bash command):
|
|
|
401
432
|
```
|
|
402
433
|
ScheduleWakeup(
|
|
403
434
|
delaySeconds: 270,
|
|
404
|
-
prompt: "/
|
|
435
|
+
prompt: "/gsd-t-unattended-watch",
|
|
405
436
|
reason: "first unattended tick"
|
|
406
437
|
)
|
|
407
438
|
```
|
package/commands/gsd-t-wave.md
CHANGED
|
@@ -74,6 +74,16 @@ node -e "const tb = require('./bin/token-budget.cjs'); const s = tb.getSessionSt
|
|
|
74
74
|
|
|
75
75
|
This calls `getSessionStatus()` (v2.0.0) which reads `.gsd-t/.context-meter-state.json` produced by the Context Meter PostToolUse hook. The returned `threshold` drives the gate logic in the Phase Agent Spawn Pattern below — it enforces the three-band stop boundary (85%) so the wave orchestrator itself never runs out of context mid-wave. When the state file is absent or stale, the call falls back to a historical heuristic from `.gsd-t/token-log.md`. Band boundaries and `modelWindowSize` are configured in `.gsd-t/context-meter-config.json` and `bin/token-budget.cjs` (THRESHOLDS constant).
|
|
76
76
|
|
|
77
|
+
## Step 0.2: Universal Auto-Pause Rule (MANDATORY — context-meter-contract v1.2.0, M37)
|
|
78
|
+
|
|
79
|
+
**If at ANY point during this command you see a `🛑 MANDATORY STOP` message in `additionalContext` (delivered by the Context Meter PostToolUse hook), you MUST:**
|
|
80
|
+
1. Immediately stop all work — do NOT advance to the next phase, do NOT spawn the next subagent
|
|
81
|
+
2. Run `/user:gsd-t-pause` to save exact position
|
|
82
|
+
3. Tell the user to run `/clear` then `/user:gsd-t-resume`
|
|
83
|
+
4. STOP — do not continue
|
|
84
|
+
|
|
85
|
+
This has the same enforcement weight as the Destructive Action Guard. The signal means the context window is at or above the configured threshold (default 75%) and continuing risks hitting the runtime's ~95% `/compact` wall.
|
|
86
|
+
|
|
77
87
|
## Step 1: Load State (Lightweight)
|
|
78
88
|
|
|
79
89
|
Read ONLY:
|
package/docs/architecture.md
CHANGED
|
@@ -345,7 +345,7 @@ Closes the M35 parent/child race in `bin/headless-auto-spawn.js`. When the runwa
|
|
|
345
345
|
|
|
346
346
|
### Resume Auto-Reattach
|
|
347
347
|
|
|
348
|
-
`/user:gsd-t-resume` Step 0 checks for a live supervisor before any other resume logic. If `supervisor.pid` exists and `kill -0` succeeds and `state.json.status` is non-terminal, the resume command skips normal resume flow entirely, prints the current watch block, and calls `ScheduleWakeup(270, '/
|
|
348
|
+
`/user:gsd-t-resume` Step 0 checks for a live supervisor before any other resume logic. If `supervisor.pid` exists and `kill -0` succeeds and `state.json.status` is non-terminal, the resume command skips normal resume flow entirely, prints the current watch block, and calls `ScheduleWakeup(270, '/gsd-t-unattended-watch', ...)`. The user transparently re-enters the watch loop without any manual step.
|
|
349
349
|
|
|
350
350
|
---
|
|
351
351
|
|
package/docs/requirements.md
CHANGED
|
@@ -287,9 +287,9 @@
|
|
|
287
287
|
|
|
288
288
|
**M36 Functional Requirements:**
|
|
289
289
|
- **REQ-079**: `bin/gsd-t-unattended.js` implements the supervisor relay loop: spawn worker → await exit → post-worker safety check → next iter. State written atomically to `.gsd-t/.unattended/state.json`. Contract: `unattended-supervisor-contract.md` v1.0.0.
|
|
290
|
-
- **REQ-080**: `commands/gsd-t-unattended.md` pre-flights branch + dirty tree, spawns supervisor detached, polls for PID readiness, displays initial watch block, and calls `ScheduleWakeup(270, '/
|
|
290
|
+
- **REQ-080**: `commands/gsd-t-unattended.md` pre-flights branch + dirty tree, spawns supervisor detached, polls for PID readiness, displays initial watch block, and calls `ScheduleWakeup(270, '/gsd-t-unattended-watch')`.
|
|
291
291
|
- **REQ-081**: `commands/gsd-t-unattended-watch.md` implements the watch tick decision tree (§8 of contract): reads PID → liveness probe → reads state.json → reschedule or terminal report.
|
|
292
|
-
- **REQ-082**: `commands/gsd-t-resume.md` Step 0 checks `supervisor.pid` before any other resume logic. If live + non-terminal: skip normal resume, print watch block, call `ScheduleWakeup(270, '/
|
|
292
|
+
- **REQ-082**: `commands/gsd-t-resume.md` Step 0 checks `supervisor.pid` before any other resume logic. If live + non-terminal: skip normal resume, print watch block, call `ScheduleWakeup(270, '/gsd-t-unattended-watch')`.
|
|
293
293
|
- **REQ-083**: Supervisor relay architecture ensures each worker gets a fresh context window. No compaction state carries over between workers — only `.gsd-t/` milestone state files.
|
|
294
294
|
- **REQ-084**: `bin/gsd-t-unattended-safety.js` exports: `checkGitBranch`, `checkWorktreeCleanliness`, `validateState`, `checkIterationCap`, `checkWallClockCap`, `detectBlockerSentinel`, `detectGutter`. Called at all 4 supervisor hook points.
|
|
295
295
|
- **REQ-085**: `bin/gsd-t-unattended-platform.js` exports: `spawnSupervisor`, `preventSleep`, `releaseSleep`, `notify`, `resolveClaudePath`. Windows: `preventSleep` is a documented no-op. Windows caveats documented in `docs/unattended-windows-caveats.md`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tekyzinc/gsd-t",
|
|
3
|
-
"version": "3.10
|
|
3
|
+
"version": "3.11.10",
|
|
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",
|