@tekyzinc/gsd-t 5.6.11 → 5.8.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 +43 -0
- package/README.md +2 -2
- package/bin/gsd-t.js +33 -4
- package/commands/gsd-t-architect.md +261 -18
- package/commands/gsd-t-help.md +4 -2
- package/package.json +1 -1
- package/scripts/gsd-t-worktree-guard.js +183 -0
- package/templates/CLAUDE-global.md +5 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,49 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to GSD-T are documented here. Updated with each release.
|
|
4
4
|
|
|
5
|
+
## [5.8.10] - 2026-08-05
|
|
6
|
+
|
|
7
|
+
### Changed — the architect now interviews you before it assesses
|
|
8
|
+
|
|
9
|
+
The architect sometimes focused on the wrong thing, or skipped something because it decided the thing was out of scope. Rather than patch that from the description, 16 real architect runs across 10 sessions were audited. They contained **21 corrections the user had to make — and 13 of those were facts the user already held and would have given for free.** Only 4 of 16 runs needed no correction.
|
|
10
|
+
|
|
11
|
+
| Failure | n | What it sounded like |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| Re-derived an already-settled rule | 8 | *"That rule was also implemented, at least I thought it was last week."* |
|
|
14
|
+
| Asserted real-world behavior the user knew was false | 5 | *"I believe you're wrong. When scrolling the feed…"* — it had measured a saved page and described it as the live one |
|
|
15
|
+
| Weighed a trade-off the user had already closed | 3 | *"Stop telling me rather or two. Just tell me how we get to one."* |
|
|
16
|
+
| Output the user could not read | 3 | *"Try again without any jargon. I don't understand the grid."* |
|
|
17
|
+
| Analyzed an adjacent thing | 2 | *"Either we're having a miscommunication or you didn't find all the bugs."* |
|
|
18
|
+
| Excluded a dimension held in scope | 2 | *"Now consider capture performance in the context of these changes."* |
|
|
19
|
+
|
|
20
|
+
The top two — 13 of 21 — share one cause: **the architect reasoned where it should have asked.**
|
|
21
|
+
|
|
22
|
+
- `commands/gsd-t-architect.md`: a grounding loop now runs before the Six-Stage Pass — read the code and the standing rules → **interview** → **research** → loop, **max 3 cycles** (1-2 expected). The interview leads by showing its read of *how the thing works today* as a plain-English flow for the user to confirm or correct, which kills a wrong premise in one line instead of after a wasted pass. Two questions are mandatory every run, aimed at the top two failures: what is already settled and not open for re-litigation (echoing the harvested rules back so a stale one gets corrected cheaply), and what is true at runtime that the code cannot show. The rest branch by target type — bug-hunt, performance, plan-review, subsystem-audit, security. It gathers the evidence it can and explicitly names what it cannot get from the repo.
|
|
23
|
+
- **Research is gated on confidence and runs after the interview, never before** — so it cannot anchor the questions on an external solution shape, and so no search is spent on a question the user would answer in one line. The audit found no correction of the form "you didn't know how others solve this," so research is real but secondary to asking.
|
|
24
|
+
- **When external practice contradicts a standing project rule, the rule wins and the conflict is named in one line.** The narrow case where a fallback is genuinely warranted requires all three of: the primary path fails a high percentage of the time, the cause is outside our control, and completing the workflow is critical. Otherwise the answer is a halt.
|
|
25
|
+
- **At the 3-cycle cap the architect asks whether to halt or proceed** with the uncertainty flagged. It does not decide that alone.
|
|
26
|
+
- The interview runs in the main session and the pass in a fresh subagent — a subagent cannot ask anything mid-run, and relaying questions out as pings had already produced babysitting. The confirmed grounding is threaded into the subagent so it never re-derives what was just settled.
|
|
27
|
+
- Eight standing checks distilled from the corrections now run every pass: audit *existing* fallbacks as a root-cause candidate, no data left in limbo, delete the legacy path in the same change, two mechanisms doing one job means route to one rather than present a trade-off, state a verification path, production-data impact before a migration or merge, label any claim resting on a snapshot, and cover the always-in-scope dimensions. Output constraints are hard: plain English in the lead, no file:line grids, no shorthand.
|
|
28
|
+
- `.gsd-t/contracts/architects-oversight-contract.md` → v1.1.0: adds §0 (the grounding loop) and guards G-8..G-11.
|
|
29
|
+
- Two new flags: `--no-interview`, `--no-research`.
|
|
30
|
+
- **Fixed in passing:** the command still pointed at the pre-M99 graph path `.gsd-t/graph.db` instead of `.gsd-t/graphDB/graph.db` — the same false negative recorded in 5.2.11, which had been silently disabling the architect's own reuse check.
|
|
31
|
+
|
|
32
|
+
The measure of a run is now what happens after it: a run succeeds if the build it directed needed few follow-ups. A well-formed report that led to rework is a failed run. Suite 3120/0/13-skip.
|
|
33
|
+
|
|
34
|
+
## [5.7.10] - 2026-08-03
|
|
35
|
+
|
|
36
|
+
### Added — one session per working tree, enforced (M105)
|
|
37
|
+
|
|
38
|
+
Several sessions ran against one project, each told to use its own git worktree. Three did. One worked directly in the main project folder, and its uncommitted work interleaved with another session's on the same branch — neither side able to commit or merge without dragging in or losing the other's half-finished milestone. The instruction was given; nothing enforced it.
|
|
39
|
+
|
|
40
|
+
- `scripts/gsd-t-worktree-guard.js`: a PreToolUse hook on Write/Edit. Blocks an edit in the MAIN working tree when another GSD-T session is live there, and prints the exact `git worktree add` command — including the stash-and-carry form when the session already has uncommitted work. Silent when alone in main (working there solo is fine), silent inside a worktree, silent outside a git repo, and fail-open on any internal error: it detects a collision, it does not gate correctness.
|
|
41
|
+
- Liveness reuses the per-session `.gsd-t/heartbeat-<id>.jsonl` file that the SessionStart/Stop/SessionEnd hooks already write INTO the tree the session works in. Its location is the claim; its modification time is the signal. No new bookkeeping.
|
|
42
|
+
- **The liveness window is the load-bearing detail.** A first pass using two hours read three long-closed sessions as live and would have fired on a user working alone — the false positive that teaches someone to disable a guard. The window is five minutes: a working session writes constantly.
|
|
43
|
+
- `bin/gsd-t.js`: the architect-hook registrar is generalized to take a marker + command, so registering a second Write|Edit hook needed no duplicated code. Registered on install, stripped on uninstall.
|
|
44
|
+
- `test/m105-worktree-guard.test.js`: 13 tests. The fail-open case caught a real hole — unparseable hook input fell back to `process.cwd()`, so the guard judged whichever directory it happened to launch from rather than the one under edit. Unreadable input now means no decision.
|
|
45
|
+
|
|
46
|
+
Opt out per project with `.gsd-t/worktree-guard-config.json` `{"enabled": false}`. An invalid config leaves the guard ON. Suite 3120/0/13-skip.
|
|
47
|
+
|
|
5
48
|
## [5.6.11] - 2026-08-02
|
|
6
49
|
|
|
7
50
|
### Fixed — four bugs that reported a healthy code graph as broken (M104)
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# GSD-T: Contract-Driven Development for Claude Code
|
|
2
2
|
|
|
3
|
-
**v5.
|
|
3
|
+
**v5.8.10** - 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.
|
|
@@ -200,7 +200,7 @@ This will replace changed command files, back up your CLAUDE.md if customized, a
|
|
|
200
200
|
| `/gsd-t-partition` | Decompose into domains + contracts | In wave |
|
|
201
201
|
| `/gsd-t-plan` | Create atomic task lists per domain (tasks auto-split to fit one context window) | In wave |
|
|
202
202
|
| `/gsd-t-impact` | Analyze downstream effects | In wave |
|
|
203
|
-
| `/gsd-t-architect` |
|
|
203
|
+
| `/gsd-t-architect` | Interviews you first (shows its read of current behavior for confirmation, researches when unsure — max 3 cycles), then runs the Architect's Oversight Six-Stage Pass — simplest solution + reuse + traps, as plain-English pseudocode (plan-only; `--build` to auto-build, `--no-interview`/`--no-research` to skip a stage) | Manual |
|
|
204
204
|
| `/gsd-t-execute` | Run tasks — task-level fresh dispatch, worktree isolation, adaptive replanning | In wave |
|
|
205
205
|
| `/gsd-t-test-sync` | Sync tests with code changes | In wave |
|
|
206
206
|
| `/gsd-t-qa` | QA agent — test generation, execution, gap reporting | Auto-spawned |
|
package/bin/gsd-t.js
CHANGED
|
@@ -494,6 +494,17 @@ const ARCHITECT_HOOK_MARKER = "gsd-t-architect-oversight-guard";
|
|
|
494
494
|
const ARCHITECT_HOOK_COMMAND =
|
|
495
495
|
'bash -c \'[ -f "$(npm root -g)/@tekyzinc/gsd-t/scripts/gsd-t-architect-oversight-guard.js" ] && node "$(npm root -g)/@tekyzinc/gsd-t/scripts/gsd-t-architect-oversight-guard.js" || true\'';
|
|
496
496
|
|
|
497
|
+
// M105 — worktree-collision PreToolUse hook on Write|Edit. Same global-package
|
|
498
|
+
// pattern. Blocks an edit when ANOTHER GSD-T session is live in the SAME working
|
|
499
|
+
// tree (detected from the per-session heartbeat files already written into
|
|
500
|
+
// .gsd-t/), and tells the user the exact `git worktree add` command to run.
|
|
501
|
+
// Silent when working alone, silent inside a worktree, silent outside a repo,
|
|
502
|
+
// and fail-open on any internal error — it detects a collision, it does not gate
|
|
503
|
+
// correctness.
|
|
504
|
+
const WORKTREE_HOOK_MARKER = "gsd-t-worktree-guard";
|
|
505
|
+
const WORKTREE_HOOK_COMMAND =
|
|
506
|
+
'bash -c \'[ -f "$(npm root -g)/@tekyzinc/gsd-t/scripts/gsd-t-worktree-guard.js" ] && node "$(npm root -g)/@tekyzinc/gsd-t/scripts/gsd-t-worktree-guard.js" || true\'';
|
|
507
|
+
|
|
497
508
|
// Append entries to {projectDir}/.gitignore. Each entry added only if absent.
|
|
498
509
|
// Idempotent. Returns true if any entries were added, false otherwise.
|
|
499
510
|
function ensureGitignoreEntries(projectDir, entries) {
|
|
@@ -942,6 +953,17 @@ function configureReadInterceptHook(settingsPath) {
|
|
|
942
953
|
// read-intercept installer but on PreToolUse. The script fails-open so this is
|
|
943
954
|
// safe globally (silent in non-GSD-T projects and on prose/doc writes).
|
|
944
955
|
function configureArchitectHook(settingsPath) {
|
|
956
|
+
return configureWriteEditHook(settingsPath, ARCHITECT_HOOK_MARKER, ARCHITECT_HOOK_COMMAND, "architect");
|
|
957
|
+
}
|
|
958
|
+
|
|
959
|
+
// M105 — register the worktree-collision guard. Reuses the same registrar rather
|
|
960
|
+
// than copying it (the function below was architect-specific; generalized to take
|
|
961
|
+
// the marker + command, so a third Write|Edit hook needs no new code).
|
|
962
|
+
function configureWorktreeGuardHook(settingsPath) {
|
|
963
|
+
return configureWriteEditHook(settingsPath, WORKTREE_HOOK_MARKER, WORKTREE_HOOK_COMMAND, "worktree guard");
|
|
964
|
+
}
|
|
965
|
+
|
|
966
|
+
function configureWriteEditHook(settingsPath, marker, command, label) {
|
|
945
967
|
const targetPath = settingsPath || SETTINGS_JSON;
|
|
946
968
|
let settings = {};
|
|
947
969
|
if (fs.existsSync(targetPath)) {
|
|
@@ -949,21 +971,21 @@ function configureArchitectHook(settingsPath) {
|
|
|
949
971
|
settings = JSON.parse(fs.readFileSync(targetPath, "utf8"));
|
|
950
972
|
if (!settings || typeof settings !== "object") settings = {};
|
|
951
973
|
} catch {
|
|
952
|
-
warn(
|
|
974
|
+
warn(`settings.json has invalid JSON — cannot configure ${label} hook`);
|
|
953
975
|
return { installed: false, action: "noop" };
|
|
954
976
|
}
|
|
955
977
|
}
|
|
956
978
|
if (!settings.hooks) settings.hooks = {};
|
|
957
979
|
if (!Array.isArray(settings.hooks.PreToolUse)) settings.hooks.PreToolUse = [];
|
|
958
980
|
|
|
959
|
-
const cmd =
|
|
981
|
+
const cmd = command;
|
|
960
982
|
let action = "noop";
|
|
961
983
|
let found = false;
|
|
962
984
|
for (const entry of settings.hooks.PreToolUse) {
|
|
963
985
|
if (!entry || !Array.isArray(entry.hooks)) continue;
|
|
964
986
|
for (const h of entry.hooks) {
|
|
965
987
|
if (!h || typeof h.command !== "string") continue;
|
|
966
|
-
if (h.command === cmd || h.command.includes(
|
|
988
|
+
if (h.command === cmd || h.command.includes(marker)) {
|
|
967
989
|
found = true;
|
|
968
990
|
if (h.command !== cmd) { h.command = cmd; action = "updated"; }
|
|
969
991
|
if (entry.matcher !== "Write|Edit") { entry.matcher = "Write|Edit"; action = action === "noop" ? "updated" : action; }
|
|
@@ -1007,7 +1029,7 @@ function removeInterceptHooks(settingsPath) {
|
|
|
1007
1029
|
if (!settings.hooks) return false;
|
|
1008
1030
|
|
|
1009
1031
|
const postMarkers = [GRAPH_INTERCEPT_HOOK_MARKER, READ_INTERCEPT_HOOK_MARKER];
|
|
1010
|
-
const preMarkers = [ARCHITECT_HOOK_MARKER];
|
|
1032
|
+
const preMarkers = [ARCHITECT_HOOK_MARKER, WORKTREE_HOOK_MARKER];
|
|
1011
1033
|
let removed = 0;
|
|
1012
1034
|
|
|
1013
1035
|
const stripByMarkers = (arr, markers) => {
|
|
@@ -2006,6 +2028,13 @@ async function doInstall(opts = {}) {
|
|
|
2006
2028
|
else info("Architect-oversight hook already configured");
|
|
2007
2029
|
}
|
|
2008
2030
|
|
|
2031
|
+
const wtHook = configureWorktreeGuardHook(SETTINGS_JSON);
|
|
2032
|
+
if (wtHook.installed) {
|
|
2033
|
+
if (wtHook.action === "added") success("Worktree-collision guard added (blocks a second session editing the same tree — M105)");
|
|
2034
|
+
else if (wtHook.action === "updated") success("Worktree-collision guard refreshed");
|
|
2035
|
+
else info("Worktree-collision guard already configured");
|
|
2036
|
+
}
|
|
2037
|
+
|
|
2009
2038
|
heading("Graph Engine (CGC)");
|
|
2010
2039
|
installCgc();
|
|
2011
2040
|
|
|
@@ -14,6 +14,33 @@ Unlike the plan/milestone workflow (which runs the Six-Stage Pass *while generat
|
|
|
14
14
|
this command runs it as a **standalone pass over existing work** — an already-frozen plan, a
|
|
15
15
|
messy subsystem, or a pasted description of problems.
|
|
16
16
|
|
|
17
|
+
**The measure of a good run is what happens AFTER it.** A run succeeded if the build it directed
|
|
18
|
+
finished with few follow-ups and few bug fixes. A beautiful report that led to five rounds of
|
|
19
|
+
rework is a FAILED run. Everything below exists to buy that outcome.
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
## Why the grounding loop exists (read this — it is the point of the command)
|
|
24
|
+
|
|
25
|
+
An audit of 16 real architect runs found **21 corrections the user had to make. 13 of them were
|
|
26
|
+
facts the user already held and would have given for free** — settled decisions the architect
|
|
27
|
+
re-derived and got wrong, and real-world behavior it asserted from a saved page instead of asking.
|
|
28
|
+
Only 4 of 16 runs needed no correction.
|
|
29
|
+
|
|
30
|
+
The two dominant failures, both preventable by asking first:
|
|
31
|
+
|
|
32
|
+
| Failure | Count | What it sounded like |
|
|
33
|
+
|---|---|---|
|
|
34
|
+
| **Re-derived an already-settled rule** | 8 | *"That rule was also implemented, at least I thought it was last week."* |
|
|
35
|
+
| **Asserted a real-world fact the user knew was false** | 5 | *"I believe you're wrong. When scrolling the feed…"* (reasoned from a saved HTML file, not the live page) |
|
|
36
|
+
| Weighed a trade-off the user had already closed | 3 | *"Stop telling me rather or two. Just tell me how we get to one."* |
|
|
37
|
+
| Output the user could not read | 3 | *"Try again without any jargon. I don't understand the grid."* |
|
|
38
|
+
| Analyzed an adjacent thing, missed the real one | 2 | *"Either we're having a miscommunication or you didn't find all the bugs."* |
|
|
39
|
+
| Excluded a dimension the user held in scope | 2 | *"Now consider capture performance in the context of these changes."* |
|
|
40
|
+
|
|
41
|
+
So: **ask before you reason.** The architect is not being thorough when it derives a constraint
|
|
42
|
+
the user could have stated in one line — it is guessing in an expensive way.
|
|
43
|
+
|
|
17
44
|
---
|
|
18
45
|
|
|
19
46
|
## Argument Parsing
|
|
@@ -26,6 +53,11 @@ Parse `$ARGUMENTS`:
|
|
|
26
53
|
(do not stop to ask). Default (no flag): produce the plan, then OFFER to build.
|
|
27
54
|
- **`--chat-only`** — report in the session only; do NOT write a pseudocode file to disk.
|
|
28
55
|
Default: write the pseudocode artifact to `.gsd-t/pseudocode/`.
|
|
56
|
+
- **`--no-interview`** — skip the interview/research loop (Steps 1-3) and go straight to the
|
|
57
|
+
Six-Stage Pass. Step 0 (reading code + harvesting standing rules) ALWAYS runs — it costs the user
|
|
58
|
+
nothing. For a target already tightly scoped in the same session. Default: run the loop.
|
|
59
|
+
- **`--no-research`** — run the interview but never the external research step. For work that is
|
|
60
|
+
purely internal to this repo. Default: research when confidence is low.
|
|
29
61
|
|
|
30
62
|
If `$TARGET` is empty, ask the user what to assess. Do not guess.
|
|
31
63
|
|
|
@@ -33,29 +65,197 @@ If `$TARGET` is empty, ask the user what to assess. Do not guess.
|
|
|
33
65
|
|
|
34
66
|
## Behavior contract (what the user asked for)
|
|
35
67
|
|
|
36
|
-
1. **
|
|
68
|
+
1. **Ground before you assess.** Read → interview → research → loop until confident (Steps 1-3),
|
|
69
|
+
THEN run the Six-Stage Pass. Never open with the pass.
|
|
70
|
+
2. **Default = plan, then offer to build.** Produce the assessment + plan, then end by asking
|
|
37
71
|
"Build the simplest solution now?" — unless `--build` was passed, in which case auto-build.
|
|
38
|
-
|
|
72
|
+
3. **`--build` = auto-build after planning.** Assess → plan → implement the simplest solution,
|
|
39
73
|
without pausing — BUT still print the findings + plan summary to the session first.
|
|
40
|
-
|
|
74
|
+
4. **ALWAYS print a session summary** — even in auto-build mode. The user scans the conversation
|
|
41
75
|
for anything that catches their eye while it keeps moving. Never silently proceed.
|
|
42
76
|
|
|
43
77
|
---
|
|
44
78
|
|
|
45
|
-
## Step
|
|
79
|
+
## Step 0: Read first — arrive with a draft understanding, not questions
|
|
80
|
+
|
|
81
|
+
**Do this in the MAIN session** (not a subagent — subagents cannot talk to the user mid-run).
|
|
82
|
+
|
|
83
|
+
Gather everything you can WITHOUT the user, so the interview spends their time only on what code
|
|
84
|
+
cannot tell you:
|
|
85
|
+
|
|
86
|
+
- **Read the target** — the named code/plan/files, their callers, the tests over them.
|
|
87
|
+
- **Query the code graph** (`gsd-t graph`) for the reuse and duplication checks. Absent/empty
|
|
88
|
+
graph → grep instead and say so LOUDLY (reuse-detection is reduced; never a silent
|
|
89
|
+
"nothing found"). A BROKEN graph is a HALT, not a grep fallback.
|
|
90
|
+
- **Harvest the standing rules — do NOT re-derive them.** Read, in this order:
|
|
91
|
+
- project `CLAUDE.md` + `~/.claude/CLAUDE.md` — hard constraints
|
|
92
|
+
- `.gsd-t/pseudocode/PseudoCode-*.md` for the touched area — especially each `[RULE]` guard map
|
|
93
|
+
and any `## ⚠ Divergence`
|
|
94
|
+
- `.gsd-t/contracts/` for the touched domains
|
|
95
|
+
- `.gsd-t/progress.md` Decision Log — the last ~30 days of entries touching these files
|
|
96
|
+
- **Classify the target type** (drives the interview shape — see Step 1):
|
|
97
|
+
`bug-hunt` · `perf` · `plan-review` · `subsystem-audit` · `security/stealth`.
|
|
98
|
+
- **Label every piece of evidence `LIVE` or `SNAPSHOT`.** A saved HTML file, a fixture, a stored
|
|
99
|
+
sample, a log from last week are all SNAPSHOT. **Any claim about runtime behavior that rests on
|
|
100
|
+
a SNAPSHOT must be marked as unproven and put to the user in the interview.** This is the exact
|
|
101
|
+
hole that produced *"FB only keeps about 6 to 8 posts visible at a time and virtualizes the
|
|
102
|
+
rest"* — the architect had measured a 4.5 MB saved feed and described it as the live page.
|
|
46
103
|
|
|
47
|
-
|
|
48
|
-
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Step 1: The interview — show your understanding, ask only what code can't answer
|
|
107
|
+
|
|
108
|
+
**The interview is adaptive. It is NOT a fixed questionnaire.** Its shape follows the target type
|
|
109
|
+
and what Step 0 left genuinely unresolved. Never ask a question you already answered by reading.
|
|
110
|
+
|
|
111
|
+
### 1a — Lead with pseudocode of how it works TODAY (the confirmation artifact)
|
|
112
|
+
|
|
113
|
+
Before any question, show the user **your read of the current behavior as a plain-English flow**,
|
|
114
|
+
in the house style (nested decision tree, `pseudocode-source-of-truth-contract.md` §1.1). Then
|
|
115
|
+
show **what you believe correct looks like**. Keep both short — this is a confirmation device, not
|
|
116
|
+
the deliverable.
|
|
117
|
+
|
|
118
|
+
This is the single highest-value move in the command: it surfaces a wrong premise in one line of
|
|
119
|
+
the user's time instead of after a full wasted pass.
|
|
120
|
+
|
|
121
|
+
```
|
|
122
|
+
Here's how I believe it works today:
|
|
123
|
+
|
|
124
|
+
A comment arrives on screen
|
|
125
|
+
Do we already know the post's author:
|
|
126
|
+
Yes: Attach the comment to that post
|
|
127
|
+
No: Read the post header now
|
|
128
|
+
Header readable:
|
|
129
|
+
Yes: Cache it, attach the comment
|
|
130
|
+
No: Drop the comment
|
|
131
|
+
|
|
132
|
+
And what I believe SHOULD happen: … (the same, in flow form)
|
|
133
|
+
|
|
134
|
+
Is that right? Correct anything wrong before I go further.
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### 1b — Ask the questions that matter for THIS target type
|
|
138
|
+
|
|
139
|
+
Ask **2-5 questions max**, drawn from the branch below. Prefer specific over generic. Skip any the
|
|
140
|
+
user's own request already answered.
|
|
141
|
+
|
|
142
|
+
**ALWAYS ask, every type (these are the top-2 failure modes):**
|
|
143
|
+
1. **Settled ground** — *"What decisions here are already made and NOT open for re-litigation?"*
|
|
144
|
+
Then **echo back the constraints you harvested in Step 0** so a stale one gets corrected in one
|
|
145
|
+
line. Format: *"I'm treating these as fixed: [list]. Correct any that have changed."*
|
|
146
|
+
2. **What the code can't tell me** — *"What do you know about how this behaves in the real world
|
|
147
|
+
that I can't read from the code?"* — and name every SNAPSHOT-derived claim explicitly:
|
|
148
|
+
*"I'm inferring X from a saved page, not the live system. Is X actually true?"*
|
|
149
|
+
|
|
150
|
+
**Type-specific branches:**
|
|
151
|
+
|
|
152
|
+
| Target type | Ask about |
|
|
153
|
+
|---|---|
|
|
154
|
+
| **bug-hunt** | The observable symptom in the user's own words — *"what would you see on screen when it's fixed?"* · Existing evidence: *"do you have screenshots, saved HTML, traces, a prod query I should reason from?"* · Whether a thing you're about to call unavoidable really is — *"is this a genuine page/system limit, or a fallback hiding a fixable bug?"* · The business rule in the user's words when the bug touches one |
|
|
155
|
+
| **perf** | Is the evidence a saved page or the live system? · What triggers the hot path, how often, driven by what? · **How will we measure before/after, and on whose machine?** (a perf verdict with no verification path is incomplete) · Re-assert the stealth/no-timer constraints |
|
|
156
|
+
| **plan-review** | Deploy/merge order and who is using the system while this lands · Does it touch production data — is cleanup implied? · **Is any part of this plan already built?** (a past run proposed building things that already existed and were running) |
|
|
157
|
+
| **subsystem-audit** | *"Do you want a map of what's there, or the single path to one clean design?"* · *"Which of these findings would be re-litigating something you already decided?"* |
|
|
158
|
+
| **security/stealth** | Rank by what — risk, or the operational thing the user actually feels (smoothness, cost)? · Which other dimensions must this answer also satisfy? |
|
|
159
|
+
|
|
160
|
+
### 1c — Get the evidence you cannot gather yourself
|
|
161
|
+
|
|
162
|
+
You gather what you can; you ASK for the rest. Both, in the same breath:
|
|
49
163
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
164
|
+
> *"I pulled these myself: [files/functions/fixtures]. I can't get these from the repo — can you
|
|
165
|
+
> provide them: [a captured HTML of a post that missed comments, a screenshot of the failure,
|
|
166
|
+
> the prod row for order 4471]?"*
|
|
167
|
+
|
|
168
|
+
Never silently proceed without evidence you know you need. Naming the gap is the job.
|
|
169
|
+
|
|
170
|
+
---
|
|
171
|
+
|
|
172
|
+
## Step 2: Research — only when you are not confident, and only after the interview
|
|
173
|
+
|
|
174
|
+
**Gate:** after the interview, score your confidence in **the problem/solution landscape** — do you
|
|
175
|
+
know how this class of problem is solved well, or are you about to invent an approach from your own
|
|
176
|
+
priors? **Confident → skip research entirely.** Not confident → research.
|
|
177
|
+
|
|
178
|
+
Research runs AFTER the interview (never before) so it does not anchor you on an external solution
|
|
179
|
+
shape before you know the user's actual constraints — and so you never spend a search on a question
|
|
180
|
+
the user would have answered in one line.
|
|
181
|
+
|
|
182
|
+
**What to research** — how others have solved *this class of problem* recently: current library or
|
|
183
|
+
platform behavior, known-good patterns, known failure modes, anything time-varying you'd otherwise
|
|
184
|
+
assert from memory. Cite what you find (URL + date) per the auto-research rule; a time-varying
|
|
185
|
+
external fact stated without a source is a guess.
|
|
186
|
+
|
|
187
|
+
**When research contradicts a standing rule (e.g. the common solution uses a fallback, the project
|
|
188
|
+
bans fallbacks): the user's rule wins, and you name the conflict in ONE line.** Not a debate:
|
|
189
|
+
|
|
190
|
+
> *"The usual solution here catches the failure and retries with a default. Your no-fallback rule
|
|
191
|
+
> bars that; the compliant version halts and reports instead. Say so if you want the fallback."*
|
|
192
|
+
|
|
193
|
+
**The rare case where a fallback IS right — and how to tell.** The no-fallback rule exists because
|
|
194
|
+
90%+ of fallbacks written were covering edge cases that never or rarely happen, papered over
|
|
195
|
+
easily-fixable bugs, and created unpredictable downstream damage. It does not exist because
|
|
196
|
+
fallbacks are never correct. A fallback is genuinely warranted only when **all** of these hold:
|
|
197
|
+
|
|
198
|
+
- the primary path fails a **high** percentage of the time, and
|
|
199
|
+
- the cause is **outside our control** (a third party, a network, a platform behavior we cannot
|
|
200
|
+
fix), and
|
|
201
|
+
- completing the workflow is **critical** — stopping is worse than degrading.
|
|
202
|
+
|
|
203
|
+
If all three hold, propose it explicitly with the evidence for each. If any fails — especially if
|
|
204
|
+
the failure is a bug we could just fix — the answer is a **HALT**, not a fallback.
|
|
55
205
|
|
|
56
206
|
---
|
|
57
207
|
|
|
58
|
-
## Step
|
|
208
|
+
## Step 3: Loop until confident — max 3 cycles
|
|
209
|
+
|
|
210
|
+
Interview and research feed each other. After research, if new questions surfaced, **go back and
|
|
211
|
+
ask them**. If those answers open a new research gap, research again. Continue until you are
|
|
212
|
+
confident you can direct a build that will not need rework.
|
|
213
|
+
|
|
214
|
+
- **Cycle = one interview round (+ its research, if any).** Hard cap: **3 cycles.**
|
|
215
|
+
- **Expected: 1-2 cycles resolve it ~90% of the time.** Needing 3 is a signal the target was
|
|
216
|
+
poorly bounded — say so.
|
|
217
|
+
- **Do not pad.** If you are confident after the first round, stop and run the pass. Extra rounds
|
|
218
|
+
cost the user's time and buy nothing.
|
|
219
|
+
- **At the cap, if you are STILL not confident: stop and ask the user which they want** — halt the
|
|
220
|
+
run, or proceed with the uncertainty flagged. Present the remaining unresolved questions and why
|
|
221
|
+
you could not resolve them, then let them choose. Do not decide this yourself.
|
|
222
|
+
|
|
223
|
+
**Only when confident (or when the user says proceed) do you move to the Six-Stage Pass.**
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Step 4: Launch the architect via a Task subagent
|
|
228
|
+
|
|
229
|
+
Give the assessment a fresh context window. Spawn ONE Task subagent (`model: opus`) — this is
|
|
230
|
+
high-stakes design judgment, top tier.
|
|
231
|
+
|
|
232
|
+
**Pass it the CONFIRMED GROUNDING from Steps 0-3, not just the raw target.** This is what the
|
|
233
|
+
interview was for; a subagent that has to re-derive it will make the same mistakes again. Include:
|
|
234
|
+
|
|
235
|
+
- `$TARGET` and this protocol
|
|
236
|
+
- The **target type** (bug-hunt / perf / plan-review / subsystem-audit / security-stealth)
|
|
237
|
+
- The **confirmed current-behavior flow** the user signed off in Step 1a — plus any correction
|
|
238
|
+
they made to it (their correction is now a FACT, not a hypothesis)
|
|
239
|
+
- The **standing rules** the user confirmed as fixed — flagged **NOT open for re-litigation**
|
|
240
|
+
- The **real-world facts the user supplied** that code cannot show — each marked as user-asserted
|
|
241
|
+
- The **evidence inventory**, each item labelled `LIVE` or `SNAPSHOT`, plus anything the user
|
|
242
|
+
provided during the interview
|
|
243
|
+
- The **research findings** with citations, if research ran — including any named rule-vs-practice
|
|
244
|
+
conflict
|
|
245
|
+
- The **dimensions confirmed in scope** (performance, stealth, prod-data impact, deploy order …)
|
|
246
|
+
- Any question the user declined to answer, marked **UNRESOLVED — do not assume**
|
|
247
|
+
|
|
248
|
+
The subagent treats all of the above as settled input. If its analysis contradicts a confirmed
|
|
249
|
+
item, that is a finding to SURFACE, never a premise to quietly overturn.
|
|
250
|
+
|
|
251
|
+
Graph note: if a code graph exists (`.gsd-t/graphDB/graph.db` — resolve via
|
|
252
|
+
`bin/gsd-t-graph-store-resolver.cjs`, never hardcode the path), the subagent uses `gsd-t graph` for
|
|
253
|
+
reuse/caller queries (Stage 3 + Stage 5 duplication check). If absent, it greps/reads and says so
|
|
254
|
+
LOUDLY (reuse-detection is reduced — never a silent "nothing found").
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## Step 5: The subagent runs the Six-Stage Pass (with EVIDENCE, never conviction)
|
|
59
259
|
|
|
60
260
|
The subagent works through the six stages IN ORDER. Each can kill or reshape the plan. Every
|
|
61
261
|
"am I sure?" is answered by looking (grep / Read / graph), not by asserting.
|
|
@@ -81,10 +281,16 @@ The subagent works through the six stages IN ORDER. Each can kill or reshape the
|
|
|
81
281
|
hides inside a simplification.)
|
|
82
282
|
6b. **NO-FALLBACK-EVER** — Does the design add ANY fallback (anything that CONTINUES after a
|
|
83
283
|
failure: catch-and-continue, `|| default`, silent degrade, try-X-else-Y where Y masks X
|
|
84
|
-
failing)? If yes, do NOT design it in — surface it as an OPEN QUESTION for the user
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
284
|
+
failing)? If yes, do NOT design it in — surface it as an OPEN QUESTION for the user. The
|
|
285
|
+
straight-line process that produces the result is the goal; where it can fail, prefer a
|
|
286
|
+
**HALT** (stop + demand fix), which is NOT a fallback.
|
|
287
|
+
**The rare warranted case — all three must hold:** the primary path fails a HIGH percentage of
|
|
288
|
+
the time · the cause is OUTSIDE our control (third party / network / platform behavior we
|
|
289
|
+
cannot fix) · completing the workflow is CRITICAL (stopping is worse than degrading). Propose
|
|
290
|
+
it with evidence for each condition. If any fails — especially if the failure is a bug we could
|
|
291
|
+
simply fix — the answer is a HALT. **Also audit the EXISTING code in the target for fallbacks
|
|
292
|
+
that are themselves the bug**; a past run's root cause was literally *"it's your fallbacks that
|
|
293
|
+
are screwing up the system."* (See CLAUDE.md § No-Fallback-Ever Doctrine.)
|
|
88
294
|
7. **SIMPLY-STATED** (clarity gate — the review is NOT done until this passes) — state every
|
|
89
295
|
finding and the verdict SIMPLY: precise and complete, but every word load-bearing, the logic
|
|
90
296
|
in a straight line, ZERO jargon standing in for a clear idea, no nested clauses hiding a
|
|
@@ -95,9 +301,23 @@ The subagent works through the six stages IN ORDER. Each can kill or reshape the
|
|
|
95
301
|
A stage the subagent cannot answer with evidence is a HALT — surface it as an open question for
|
|
96
302
|
the user, do not paper over it with a guess.
|
|
97
303
|
|
|
304
|
+
**Standing checks — run these every pass, without being told.** Each was a correction the user had
|
|
305
|
+
to make by hand in a past run. Treat a violation as a finding:
|
|
306
|
+
|
|
307
|
+
| Check | Why |
|
|
308
|
+
|---|---|
|
|
309
|
+
| **Is a fallback the root cause here?** Audit existing fallbacks in the target, not just new ones | *"It's your fallbacks that are screwing up the system"* |
|
|
310
|
+
| **Does the design leave data in limbo** (quarantined, held, parked for later recovery)? Prefer: record a trace and drop | *"Never quarantine because nothing quarantined would ever be recovered"* |
|
|
311
|
+
| **Does it leave a legacy path alive alongside the new one?** Deleting the old path belongs in the same change | *"I want all legacy code removed… which keeps happening over and over again"* |
|
|
312
|
+
| **Two mechanisms doing one job** → propose the route to ONE, don't present it as a trade-off | *"Stop telling me rather or two. Just tell me how we get to one."* |
|
|
313
|
+
| **Does it state a verification path** — how we'd prove it worked, and on whose machine? | *"How do you analyze and verify?"* |
|
|
314
|
+
| **Production-data impact + cleanup**, before any migration or merge | asked unprompted across three runs |
|
|
315
|
+
| **Is any claim about live behavior resting on a SNAPSHOT?** Label it; never state it as observed | the saved-feed virtualization error |
|
|
316
|
+
| **Are the always-in-scope dimensions covered** — performance on user-facing paths, plus any project hard-constraint (stealth, security, cost)? | *"Now consider capture performance in the context of these changes"* |
|
|
317
|
+
|
|
98
318
|
---
|
|
99
319
|
|
|
100
|
-
## Step
|
|
320
|
+
## Step 6: The subagent produces the output
|
|
101
321
|
|
|
102
322
|
**A — Plain-English pseudocode** (the artifact), in the house style defined by contract
|
|
103
323
|
`.gsd-t/contracts/pseudocode-source-of-truth-contract.md` **§1.1** and the mold
|
|
@@ -127,6 +347,12 @@ in the flow). For each "what it does today" flow, say **why it does what it does
|
|
|
127
347
|
explicitly if it's a "got complicated over time" accretion (mechanisms stacked by successive
|
|
128
348
|
fixes).
|
|
129
349
|
|
|
350
|
+
Also below the divider: **`## What I confirmed with you`** — the grounding record. The
|
|
351
|
+
user-confirmed current behavior, the rules held fixed, the real-world facts they supplied, the
|
|
352
|
+
research sources (if any), and anything left unresolved. `## What it does today` must match what
|
|
353
|
+
the user confirmed in the interview; if the analysis later contradicted it, that belongs in
|
|
354
|
+
`## ⚠ Divergence`, not a quiet rewrite.
|
|
355
|
+
|
|
130
356
|
Worked reference: `.gsd-t/pseudocode/PseudoCode-BrokenGraphHalts.md`. Unless `--chat-only`, write
|
|
131
357
|
the artifact to `.gsd-t/pseudocode/PseudoCode-<Target>.md`, then **self-check it** with
|
|
132
358
|
`gsd-t pseudocode-style --doc <the file>` — a non-zero exit means the style is wrong; fix it
|
|
@@ -144,11 +370,19 @@ before presenting. (The same gate is FAIL-blocking in verify.)
|
|
|
144
370
|
- **Simplest solution** (one paragraph)
|
|
145
371
|
- **Traps surfaced** (each stage's kill/risk finding — especially the Stage-6 "does the fix
|
|
146
372
|
really self-heal?" check)
|
|
373
|
+
- **Grounded on** (2-4 lines, always) — what the user confirmed in the interview that the analysis
|
|
374
|
+
rests on, what research found (with sources) if it ran, and how many interview cycles it took.
|
|
375
|
+
This makes a wrong premise visible at a glance instead of buried in the reasoning.
|
|
147
376
|
- **Open questions** (any HALT stages needing the user)
|
|
148
377
|
|
|
378
|
+
**Output constraints (hard — these were real complaints):** plain English in the lead, no
|
|
379
|
+
file:line grids up top, no architect shorthand. *"Try again without any jargon. I don't understand
|
|
380
|
+
the grid."* and *"I don't understand any of this explanation… It's way too long, too wordy."* Put
|
|
381
|
+
code identifiers below the divider in the artifact, never in the summary lead.
|
|
382
|
+
|
|
149
383
|
---
|
|
150
384
|
|
|
151
|
-
## Step
|
|
385
|
+
## Step 7: Build decision
|
|
152
386
|
|
|
153
387
|
- **No `--build` flag (default):** end with the plain-English summary and ask:
|
|
154
388
|
*"Build the simplest solution now?"* — offer it, do not proceed.
|
|
@@ -175,4 +409,13 @@ The underlying assessment updates (when it writes / when a build follows):
|
|
|
175
409
|
- **Reuse over rebuild** (the doctrine obeying itself): this command is prose-driven and spawns
|
|
176
410
|
a single analysis subagent — it does NOT add a new workflow file. It reuses the existing
|
|
177
411
|
prose-command + Task-subagent pattern (like `/gsd-t-status`, `/gsd-t-impact`).
|
|
412
|
+
- **The interview runs in the MAIN session, the pass runs in a subagent.** A subagent cannot ask
|
|
413
|
+
the user anything mid-run — relaying questions out as teammate pings was tried and produced
|
|
414
|
+
babysitting (*"Is the architect still running?"*). So: ground it in the main chat where a real
|
|
415
|
+
back-and-forth works, then hand the confirmed grounding to a fresh context for the analysis.
|
|
416
|
+
- **Asking is cheaper than deriving.** The audit at the top of this file is the justification: the
|
|
417
|
+
architect's most common failure was spending reasoning on facts the user would have stated in
|
|
418
|
+
one line. When torn between inferring and asking — ask.
|
|
419
|
+
- **Cycle discipline.** 1-2 grounding cycles should cover ~90% of runs. Needing 3 means the target
|
|
420
|
+
was poorly bounded; say so rather than absorbing it silently.
|
|
178
421
|
- Standalone command — no successor in the Next-Up map.
|
package/commands/gsd-t-help.md
CHANGED
|
@@ -242,11 +242,13 @@ Use these when user asks for help on a specific command:
|
|
|
242
242
|
- **Use when**: Before making changes, to understand what might break
|
|
243
243
|
|
|
244
244
|
### architect
|
|
245
|
-
- **Summary**:
|
|
245
|
+
- **Summary**: Grounds itself with you FIRST, then runs the Architect's Oversight Six-Stage Pass (Objective → Conflict → Reuse → Simplicity → Reuse-forecast → Risk) on existing work — an already-frozen plan, a messy subsystem, or a pasted tangle of problems. Finds the simplest correct solution, what's already reusable (process or a stored value), and the traps each stage surfaces — written as plain-English pseudocode you can approve before any code.
|
|
246
|
+
- **Grounding loop (before the pass)**: reads the code + your standing rules → **interviews you** (shows its read of how the thing works today as a plain-English flow for you to confirm or correct, then asks only what code can't answer — questions branch by target type: bug / performance / plan-review / audit / security) → **researches** how others solve this class of problem, but only when it isn't confident and only after the interview → loops if new questions surface. **Max 3 cycles** (1-2 expected); still unsure at the cap → it asks whether to halt or proceed with the uncertainty flagged.
|
|
246
247
|
- **Auto-invoked**: No (standalone, on-demand)
|
|
247
|
-
- **Args**: `/gsd-t-architect "<what to assess>"` — plus `--build` (auto-build the simplest fix after planning)
|
|
248
|
+
- **Args**: `/gsd-t-architect "<what to assess>"` — plus `--build` (auto-build the simplest fix after planning), `--chat-only` (report in session, don't write a pseudocode file), `--no-interview` (skip the grounding loop when the target is already tightly scoped), `--no-research` (interview but never search externally).
|
|
248
249
|
- **Creates**: `.gsd-t/pseudocode/PseudoCode-<Target>.md` (unless `--chat-only`)
|
|
249
250
|
- **Use when**: A plan already exists (or is half-formed) and you want it interrogated for simplicity + reuse before building; or a subsystem "got complicated over time" and you want the simplest version. Default is plan-only, then it offers to build.
|
|
251
|
+
- **Why the interview**: an audit of 16 real runs found 21 corrections — 13 were facts the user already held and would have given for free (settled rules it re-derived wrong, runtime behavior it asserted from a saved page). Asking first is cheaper than deriving.
|
|
250
252
|
|
|
251
253
|
### execute
|
|
252
254
|
- **Summary**: Run tasks from plan, solo or with agent teams
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tekyzinc/gsd-t",
|
|
3
|
-
"version": "5.
|
|
3
|
+
"version": "5.8.10",
|
|
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",
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* gsd-t-worktree-guard.js — PreToolUse(Write|Edit)
|
|
6
|
+
*
|
|
7
|
+
* Stops two sessions from editing the SAME working tree at the same time.
|
|
8
|
+
*
|
|
9
|
+
* The problem it solves: a session opened in the main project folder edits files
|
|
10
|
+
* there; a second session opens in the same folder and does the same. Their
|
|
11
|
+
* uncommitted work interleaves in one tree, on one branch, and neither can commit
|
|
12
|
+
* or merge without dragging in the other's half-finished milestone. Telling each
|
|
13
|
+
* session "use a worktree" does not prevent it — nothing enforces the instruction.
|
|
14
|
+
*
|
|
15
|
+
* How it detects a live session, and why: every GSD-T session writes
|
|
16
|
+
* `.gsd-t/heartbeat-<session-id>.jsonl` INSIDE the tree it is working in, via the
|
|
17
|
+
* SessionStart/Stop/SessionEnd hooks. The file's location IS the claim (a session
|
|
18
|
+
* in a worktree writes into that worktree's own .gsd-t/), and its modification
|
|
19
|
+
* time is the liveness signal. No new bookkeeping — the file already exists.
|
|
20
|
+
*
|
|
21
|
+
* Liveness is a SHORT window (default 5 min). A working session writes constantly;
|
|
22
|
+
* one silent for longer is idle or closed. This matters more than it looks: a
|
|
23
|
+
* 2-hour window reads three closed sessions as live and fires on a user working
|
|
24
|
+
* alone, which trains them to disable the guard.
|
|
25
|
+
*
|
|
26
|
+
* Behaviour:
|
|
27
|
+
* - alone in any tree → silent, no guard (working in main alone is allowed)
|
|
28
|
+
* - main tree, another session live → BLOCK with the exact worktree command to run
|
|
29
|
+
* - inside a worktree → silent (already isolated)
|
|
30
|
+
*
|
|
31
|
+
* Fail-open by design: a guard that cannot read its inputs must not block edits.
|
|
32
|
+
* It is a collision detector, not a correctness gate — a crash here would stop
|
|
33
|
+
* legitimate work for no safety benefit.
|
|
34
|
+
*
|
|
35
|
+
* Opt out per project: .gsd-t/worktree-guard-config.json {"enabled": false}
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
const fs = require("fs");
|
|
39
|
+
const path = require("path");
|
|
40
|
+
const { execFileSync } = require("child_process");
|
|
41
|
+
|
|
42
|
+
const LIVE_WINDOW_MS = 5 * 60 * 1000;
|
|
43
|
+
|
|
44
|
+
// Returns null when the hook payload cannot be read or parsed.
|
|
45
|
+
//
|
|
46
|
+
// This must NOT fall back to an empty object: `cwd` would then default to
|
|
47
|
+
// process.cwd() and the guard would judge whatever directory it happened to be
|
|
48
|
+
// launched from — deciding about the wrong repository entirely. Caught by the
|
|
49
|
+
// fail-open test, which saw a deny emitted for garbage input.
|
|
50
|
+
// Unreadable input means NO DECISION, never a decision about the wrong tree.
|
|
51
|
+
function readHookInput() {
|
|
52
|
+
try {
|
|
53
|
+
const raw = fs.readFileSync(0, "utf8");
|
|
54
|
+
if (!raw || !raw.trim()) return null;
|
|
55
|
+
const parsed = JSON.parse(raw);
|
|
56
|
+
return parsed && typeof parsed === "object" ? parsed : null;
|
|
57
|
+
} catch (_) {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function git(args, cwd) {
|
|
63
|
+
try {
|
|
64
|
+
return execFileSync("git", args, {
|
|
65
|
+
cwd,
|
|
66
|
+
encoding: "utf8",
|
|
67
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
68
|
+
timeout: 5000,
|
|
69
|
+
}).trim();
|
|
70
|
+
} catch (_) {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// The MAIN working tree is the one whose .git is a directory. A linked worktree's
|
|
76
|
+
// .git is a FILE containing a gitdir: pointer — that is the distinction git itself
|
|
77
|
+
// uses, so it needs no parsing of `git worktree list` output.
|
|
78
|
+
function isMainWorktree(root) {
|
|
79
|
+
try {
|
|
80
|
+
return fs.statSync(path.join(root, ".git")).isDirectory();
|
|
81
|
+
} catch (_) {
|
|
82
|
+
return false;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function guardEnabled(root) {
|
|
87
|
+
try {
|
|
88
|
+
const cfg = JSON.parse(
|
|
89
|
+
fs.readFileSync(path.join(root, ".gsd-t", "worktree-guard-config.json"), "utf8")
|
|
90
|
+
);
|
|
91
|
+
return cfg.enabled !== false;
|
|
92
|
+
} catch (_) {
|
|
93
|
+
return true; // absent/invalid config → guard on
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Sessions whose heartbeat is fresh, excluding this one.
|
|
98
|
+
function liveSessions(root, selfSid, now) {
|
|
99
|
+
const dir = path.join(root, ".gsd-t");
|
|
100
|
+
let names;
|
|
101
|
+
try {
|
|
102
|
+
names = fs.readdirSync(dir);
|
|
103
|
+
} catch (_) {
|
|
104
|
+
return [];
|
|
105
|
+
}
|
|
106
|
+
const live = [];
|
|
107
|
+
for (const f of names) {
|
|
108
|
+
if (!f.startsWith("heartbeat-") || !f.endsWith(".jsonl")) continue;
|
|
109
|
+
const sid = f.slice("heartbeat-".length, -".jsonl".length);
|
|
110
|
+
if (selfSid && sid === selfSid) continue;
|
|
111
|
+
try {
|
|
112
|
+
const age = now - fs.statSync(path.join(dir, f)).mtimeMs;
|
|
113
|
+
if (age < LIVE_WINDOW_MS) live.push({ sid, ageMs: age });
|
|
114
|
+
} catch (_) { /* unreadable → not evidence of a live session */ }
|
|
115
|
+
}
|
|
116
|
+
return live.sort((a, b) => a.ageMs - b.ageMs);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function suggestWorktreeName(branch) {
|
|
120
|
+
const base = (branch || "work").replace(/[^A-Za-z0-9._-]/g, "-").replace(/^-+|-+$/g, "");
|
|
121
|
+
return base || "work";
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function main() {
|
|
125
|
+
const hook = readHookInput();
|
|
126
|
+
if (!hook) return; // no readable payload → no decision (see readHookInput)
|
|
127
|
+
const cwd = hook.cwd;
|
|
128
|
+
if (!cwd || typeof cwd !== "string") return; // no stated directory → nothing to judge
|
|
129
|
+
|
|
130
|
+
const root = git(["rev-parse", "--show-toplevel"], cwd);
|
|
131
|
+
if (!root) return; // not a git repo → nothing to guard
|
|
132
|
+
|
|
133
|
+
if (!isMainWorktree(root)) return; // already isolated in a worktree
|
|
134
|
+
if (!guardEnabled(root)) return;
|
|
135
|
+
|
|
136
|
+
const selfSid = hook.session_id || hook.sessionId || null;
|
|
137
|
+
const others = liveSessions(root, selfSid, Date.now());
|
|
138
|
+
if (others.length === 0) return; // alone → working in main is allowed
|
|
139
|
+
|
|
140
|
+
const branch = git(["rev-parse", "--abbrev-ref", "HEAD"], root) || "HEAD";
|
|
141
|
+
const project = path.basename(root);
|
|
142
|
+
const name = suggestWorktreeName(branch);
|
|
143
|
+
const wt = `${process.env.HOME}/Worktrees/${project}/${name}`;
|
|
144
|
+
const mins = Math.max(1, Math.round(others[0].ageMs / 60000));
|
|
145
|
+
const plural = others.length === 1 ? "session" : "sessions";
|
|
146
|
+
|
|
147
|
+
const reason = [
|
|
148
|
+
`Another GSD-T ${plural} (${others.length}) is working in this same folder right now — the most recent wrote ${mins} minute(s) ago.`,
|
|
149
|
+
``,
|
|
150
|
+
`Editing here means two sessions share one working tree and one branch. Their uncommitted changes interleave, and neither can commit or merge without dragging in the other's half-finished work.`,
|
|
151
|
+
``,
|
|
152
|
+
`Move to your own worktree first:`,
|
|
153
|
+
``,
|
|
154
|
+
` mkdir -p ${process.env.HOME}/Worktrees/${project}`,
|
|
155
|
+
` git worktree add ${wt} -b ${name}-$(date +%H%M)`,
|
|
156
|
+
` cd ${wt}`,
|
|
157
|
+
``,
|
|
158
|
+
`If you already have uncommitted work in this folder, carry it across:`,
|
|
159
|
+
``,
|
|
160
|
+
` git stash push -u -m "moving to a worktree"`,
|
|
161
|
+
` git worktree add ${wt} -b ${name}-$(date +%H%M)`,
|
|
162
|
+
` cd ${wt} && git stash pop`,
|
|
163
|
+
``,
|
|
164
|
+
`Working alone in the main folder is fine — this only fires when a second session is live.`,
|
|
165
|
+
`To turn it off for this project: .gsd-t/worktree-guard-config.json {"enabled": false}`,
|
|
166
|
+
].join("\n");
|
|
167
|
+
|
|
168
|
+
process.stdout.write(
|
|
169
|
+
JSON.stringify({
|
|
170
|
+
hookSpecificOutput: {
|
|
171
|
+
hookEventName: "PreToolUse",
|
|
172
|
+
permissionDecision: "deny",
|
|
173
|
+
permissionDecisionReason: reason,
|
|
174
|
+
},
|
|
175
|
+
}) + "\n"
|
|
176
|
+
);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
main();
|
|
181
|
+
} catch (_) {
|
|
182
|
+
// Fail open — never block an edit because the guard itself broke.
|
|
183
|
+
}
|
|
@@ -150,6 +150,8 @@ WHEN creating a worktree directly (git worktree add, isolation: "worktree", etc.
|
|
|
150
150
|
- Clean up with `git worktree remove` when the branch/task is done — don't leave prunable stragglers.
|
|
151
151
|
- **Exception**: harness-managed worktrees the Agent/Workflow runtime creates under the project's gitignored `.claude/worktrees/` path are the harness's own convention — leave those alone. This rule governs worktrees *you* create directly via Bash or the `isolation: "worktree"` option.
|
|
152
152
|
|
|
153
|
+
**One session per working tree (M105 — enforced).** Two sessions editing the same folder interleave their uncommitted work on one branch: neither can commit or merge without dragging in or losing the other's half-finished milestone. A PreToolUse guard (`scripts/gsd-t-worktree-guard.js`) BLOCKS a Write/Edit in the main tree when another GSD-T session is live there, and prints the exact `git worktree add` command (including the stash-and-carry form when you already have uncommitted work). Liveness comes from the per-session `.gsd-t/heartbeat-<id>.jsonl` file — location is the claim, mtime is liveness, 5-minute window. It is SILENT when you are alone in the main tree (working there solo is fine), silent inside a worktree, and fails open. Opt out per project: `.gsd-t/worktree-guard-config.json` `{"enabled": false}`.
|
|
154
|
+
|
|
153
155
|
# Destructive Action Guard (MANDATORY)
|
|
154
156
|
|
|
155
157
|
**NEVER perform destructive or structural changes without explicit user approval.** This applies at ALL autonomy levels, including Level 3.
|
|
@@ -447,10 +449,12 @@ See memory pointer: `feedback_auto_research_external_gaps`.
|
|
|
447
449
|
|
|
448
450
|
### Architect's Oversight Doctrine (M101 — governed, enforced)
|
|
449
451
|
|
|
450
|
-
**Contract:** `.gsd-t/contracts/architects-oversight-contract.md` v1.
|
|
452
|
+
**Contract:** `.gsd-t/contracts/architects-oversight-contract.md` v1.1.0 STABLE
|
|
451
453
|
|
|
452
454
|
**Never build before the design has passed the architect's interrogation.** GSD-T staffs verifiers (Red Team, QA, code-review, pre-mortem) — all asking "is this correct?" — but no seat asked "is this the *smartest, simplest* design given what we already have?" The result: the wrong thing built correctly, then thoroughly tested, then shipped (the Binvoice completeness-scan waste — a whole-page scan re-deriving a count already stored locally). This doctrine fills the empty architect seat. Sibling to the Unproven-Assumption Doctrine: that one bars unproven *facts*; this one bars unproven *necessity*.
|
|
453
455
|
|
|
456
|
+
**§Stage 0 — GROUND BEFORE YOU ASSESS (runs first; contract §0).** A field audit of 16 real architect runs found 21 user corrections, and **13 of them were facts the user already held** — settled rules the architect re-derived and got wrong ("that rule was implemented last week"), and runtime behavior it asserted from a saved page instead of asking ("I believe you're wrong. When scrolling the feed…"). **Asking is cheaper than deriving.** So before the pass: read the code AND the standing rules (CLAUDE.md constraints, `[RULE]` guard maps, contracts, recent Decision Log); label every evidence item **LIVE or SNAPSHOT** (a runtime claim resting on a snapshot is unproven); then **interview the user** — lead by showing your read of *how it works today* as a plain-English flow for confirmation, echo back the rules you're treating as fixed, and ask only what code cannot answer. **Research** (how others solve this class of problem) runs only when you're not confident, and only AFTER the interview so it can't anchor the questions. Loop interview↔research, **max 3 cycles** (1-2 expected); still unsure at the cap → ask the user whether to halt or proceed with the uncertainty flagged. A run succeeded if the build it directed needed few follow-ups — not if the report read well.
|
|
457
|
+
|
|
454
458
|
**The Six-Stage Pass — run IN ORDER before proposing or building any solution. Each stage can KILL the plan. Every "am I sure?" is answered with EVIDENCE (a grep, a Read, a graph query), never conviction — self-confidence is what produced the waste.**
|
|
455
459
|
|
|
456
460
|
1. **Objective** — What is the core objective? Why is it the core objective? *(Kills: building the wrong thing.)*
|