@tekyzinc/gsd-t 4.4.11 → 4.6.11
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 +17 -6
- package/README.md +56 -2
- package/bin/gsd-t-model-profile.cjs +480 -0
- package/bin/gsd-t-model-tier-policy.cjs +156 -1
- package/bin/gsd-t.js +128 -26
- package/commands/gsd-t-debug.md +36 -3
- package/commands/gsd-t-design-decompose.md +31 -3
- package/commands/gsd-t-doc-ripple.md +31 -3
- package/commands/gsd-t-help.md +15 -1
- package/commands/gsd-t-impact.md +31 -3
- package/commands/gsd-t-milestone.md +31 -3
- package/commands/gsd-t-partition.md +39 -3
- package/commands/gsd-t-plan.md +31 -3
- package/commands/gsd-t-prd.md +31 -3
- package/commands/gsd-t-status.md +14 -0
- package/commands/gsd-t-verify.md +35 -2
- package/commands/gsd-t-wave.md +34 -3
- package/docs/requirements.md +8 -1
- package/package.json +1 -1
- package/scripts/gsd-t-auto-route.js +106 -5
- package/scripts/gsd-t-statusline.js +72 -1
- package/templates/CLAUDE-global.md +83 -284
- package/templates/workflows/gsd-t-debug.workflow.js +7 -1
- package/templates/workflows/gsd-t-phase.workflow.js +11 -4
- package/templates/workflows/gsd-t-verify.workflow.js +7 -1
- package/templates/workflows/gsd-t-wave.workflow.js +7 -2
- package/templates/test-helpers/launch-extension.ts +0 -81
|
@@ -14,7 +14,32 @@ The agent defines the milestone — origin, goal, falsifiable success criteria
|
|
|
14
14
|
|
|
15
15
|
Read `.gsd-t/progress.md` (current version + completed milestones), `docs/requirements.md`, and `docs/architecture.md` so the new milestone is framed against existing state.
|
|
16
16
|
|
|
17
|
-
## Step 2:
|
|
17
|
+
## Step 2: Resolve the active model profile (M86 — invoke-time injection)
|
|
18
|
+
|
|
19
|
+
Before calling the Workflow, resolve the active model profile to build the `overrides` map:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Run via Bash at invoke time:
|
|
23
|
+
gsd-t model-profile resolve --json
|
|
24
|
+
# Bare form (NO --profile flag): reads .gsd-t/model-profile.json — profile AND stageOverrides
|
|
25
|
+
# (set-stage overrides MUST win — contract precedence; --profile is a config-blind diagnostic
|
|
26
|
+
# form that ZEROES stageOverrides and must never be used for invocation — Red Team M86 r3)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Resolver-failure handling (M86 — pre-mortem c2 #2):** if the resolve call fails, do NOT
|
|
30
|
+
silently proceed on the premium fallback. Either HALT with `blocked-needs-human`, or proceed
|
|
31
|
+
ONLY with a loud, surfaced warning:
|
|
32
|
+
```
|
|
33
|
+
⚠ model-profile resolver unavailable — running on PREMIUM fallback literals
|
|
34
|
+
(configured profile unknown; stale global binary may lack model-profile subcommand)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Also surface a SUCCESSFUL resolve that carries a `configError` field (the resolver returns a
|
|
38
|
+
named default + `configError` for malformed/hand-edited configs — Red Team M86): print the
|
|
39
|
+
`configError` as a visible warning naming the effective profile before proceeding. A clean-looking
|
|
40
|
+
run on a posture the user did not configure is the same silent-spend failure class.
|
|
41
|
+
|
|
42
|
+
## Step 3: Invoke the phase Workflow
|
|
18
43
|
|
|
19
44
|
```js
|
|
20
45
|
{
|
|
@@ -25,7 +50,10 @@ Read `.gsd-t/progress.md` (current version + completed milestones), `docs/requir
|
|
|
25
50
|
args: {
|
|
26
51
|
phase: "milestone",
|
|
27
52
|
projectDir: ".",
|
|
28
|
-
userInput: "$ARGUMENTS"
|
|
53
|
+
userInput: "$ARGUMENTS",
|
|
54
|
+
// M86: inject the resolved overrides map (probe + judge use this).
|
|
55
|
+
// Pass {} when the resolver failed AND you chose the loud-warning path (not halt).
|
|
56
|
+
overrides: { /* ...from resolver result.overrides, or {} on failure */ }
|
|
29
57
|
// M84 Competition Mode is AUTOMATIC — do NOT pass `competition` by default.
|
|
30
58
|
// The workflow probes (opus) and self-decides; milestone decomposition is the
|
|
31
59
|
// highest-altitude decision, so it competes whenever ≥2 genuinely different
|
|
@@ -37,7 +65,7 @@ Read `.gsd-t/progress.md` (current version + completed milestones), `docs/requir
|
|
|
37
65
|
|
|
38
66
|
**Competition Mode (automatic).** Milestone decomposition auto-competes when the probe finds ≥2 genuinely different strategies. Because a decomposition is a *coupled thesis*, the judge selects one winner whole (pick-one) and salvages only non-overlapping good line-items from the losers — it never Frankensteins. No flag needed; override with `--no-competition` / `--competition N` on explicit request. See `.gsd-t/contracts/competition-mode-contract.md`.
|
|
39
67
|
|
|
40
|
-
## Step
|
|
68
|
+
## Step 4: Interpret the result
|
|
41
69
|
|
|
42
70
|
The Workflow returns `{ status, artifacts, summary, decisions }` (plus `competition: { n, winner, ranked }` when Competition Mode ran).
|
|
43
71
|
|
|
@@ -16,7 +16,39 @@ The agent decomposes the milestone into 2–5 file-disjoint domains, writes `.gs
|
|
|
16
16
|
|
|
17
17
|
Read `.gsd-t/progress.md` to determine the active milestone and its defined scope. If a scan exists and is stale (>10 commits or >14 days), the agent refreshes the relevant dimensions before partitioning.
|
|
18
18
|
|
|
19
|
-
## Step 2:
|
|
19
|
+
## Step 2: Resolve the active model profile (M86 — invoke-time injection)
|
|
20
|
+
|
|
21
|
+
Before calling the Workflow, resolve the active model profile to build the `overrides` map:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
# Run via Bash at invoke time:
|
|
25
|
+
gsd-t model-profile resolve --json
|
|
26
|
+
# Bare form (NO --profile flag): reads .gsd-t/model-profile.json — profile AND stageOverrides
|
|
27
|
+
# (set-stage overrides MUST win — contract precedence; --profile is a config-blind diagnostic
|
|
28
|
+
# form that ZEROES stageOverrides and must never be used for invocation — Red Team M86 r3)
|
|
29
|
+
# Output: { "ok": true, "profile": "...", "overrides": { "stage-key": "concrete-model-id", ... } }
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Resolver-failure handling (M86 — SC(f), pre-mortem c2 #2):** if the resolve call fails
|
|
33
|
+
(`{ok:false}`, spawn error, or the `model-profile` subcommand is not present in the installed
|
|
34
|
+
binary), do NOT silently proceed on the premium fallback. Either:
|
|
35
|
+
- HALT with `blocked-needs-human` and explain the resolver is unavailable; OR
|
|
36
|
+
- Proceed ONLY with a **loud, surfaced warning** that names the effective posture:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
⚠ model-profile resolver unavailable — running on PREMIUM fallback literals
|
|
40
|
+
(configured profile unknown; stale global binary may lack model-profile subcommand)
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
A configured-standard project silently billing premium fable post-promo is the exact inverse of
|
|
44
|
+
the spend-switch goal. Never silently fall through.
|
|
45
|
+
|
|
46
|
+
Also surface a SUCCESSFUL resolve that carries a `configError` field (the resolver returns a
|
|
47
|
+
named default + `configError` for malformed/hand-edited configs — Red Team M86): print the
|
|
48
|
+
`configError` as a visible warning naming the effective profile before proceeding. A clean-looking
|
|
49
|
+
run on a posture the user did not configure is the same silent-spend failure class.
|
|
50
|
+
|
|
51
|
+
## Step 3: Invoke the phase Workflow
|
|
20
52
|
|
|
21
53
|
Call the `Workflow` tool with:
|
|
22
54
|
|
|
@@ -30,7 +62,11 @@ Call the `Workflow` tool with:
|
|
|
30
62
|
phase: "partition",
|
|
31
63
|
milestone: "M{NN}",
|
|
32
64
|
projectDir: ".",
|
|
33
|
-
userInput: "$ARGUMENTS"
|
|
65
|
+
userInput: "$ARGUMENTS",
|
|
66
|
+
// M86: inject the resolved overrides map so the workflow's ?? forms pick up the
|
|
67
|
+
// profile-tier assignments instead of falling back to the premium literals.
|
|
68
|
+
// Pass {} when the resolver failed AND you chose the loud-warning path (not halt).
|
|
69
|
+
overrides: { /* ...from resolver result.overrides, or {} on failure */ }
|
|
34
70
|
// M84 Competition Mode is AUTOMATIC — do NOT pass `competition` by default.
|
|
35
71
|
// The workflow runs a solution-space probe and self-decides whether to fan out
|
|
36
72
|
// N candidate partitions (judged by the file-disjointness oracle). Only pass an
|
|
@@ -42,7 +78,7 @@ Call the `Workflow` tool with:
|
|
|
42
78
|
|
|
43
79
|
**Competition Mode (automatic).** Partition auto-competes when the workflow's probe finds ≥2 genuinely different ways to carve the domains; the objective file-disjointness oracle judges the candidates and picks the most-parallelizable valid one. No flag needed. Override only on explicit request: `/gsd-t-partition --no-competition` (force single draft) or `--competition N` (force N). See `.gsd-t/contracts/competition-mode-contract.md`.
|
|
44
80
|
|
|
45
|
-
## Step
|
|
81
|
+
## Step 4: Interpret the result
|
|
46
82
|
|
|
47
83
|
The Workflow returns `{ status, artifacts, summary, decisions }` (plus `competition: { n, winner, ranked }` when Competition Mode ran).
|
|
48
84
|
|
package/commands/gsd-t-plan.md
CHANGED
|
@@ -14,7 +14,32 @@ For each domain, the agent writes atomic `tasks.md` entries in parallel-executio
|
|
|
14
14
|
|
|
15
15
|
Read `.gsd-t/progress.md` and each domain's `scope.md`/`constraints.md`. The partition output is the input to planning.
|
|
16
16
|
|
|
17
|
-
## Step 2:
|
|
17
|
+
## Step 2: Resolve the active model profile (M86 — invoke-time injection)
|
|
18
|
+
|
|
19
|
+
Before calling the Workflow, resolve the active model profile to build the `overrides` map:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Run via Bash at invoke time:
|
|
23
|
+
gsd-t model-profile resolve --json
|
|
24
|
+
# Bare form (NO --profile flag): reads .gsd-t/model-profile.json — profile AND stageOverrides
|
|
25
|
+
# (set-stage overrides MUST win — contract precedence; --profile is a config-blind diagnostic
|
|
26
|
+
# form that ZEROES stageOverrides and must never be used for invocation — Red Team M86 r3)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Resolver-failure handling (M86 — pre-mortem c2 #2):** if the resolve call fails, do NOT
|
|
30
|
+
silently proceed on the premium fallback. Either HALT with `blocked-needs-human`, or proceed
|
|
31
|
+
ONLY with a loud, surfaced warning:
|
|
32
|
+
```
|
|
33
|
+
⚠ model-profile resolver unavailable — running on PREMIUM fallback literals
|
|
34
|
+
(configured profile unknown; stale global binary may lack model-profile subcommand)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Also surface a SUCCESSFUL resolve that carries a `configError` field (the resolver returns a
|
|
38
|
+
named default + `configError` for malformed/hand-edited configs — Red Team M86): print the
|
|
39
|
+
`configError` as a visible warning naming the effective profile before proceeding. A clean-looking
|
|
40
|
+
run on a posture the user did not configure is the same silent-spend failure class.
|
|
41
|
+
|
|
42
|
+
## Step 3: Invoke the phase Workflow
|
|
18
43
|
|
|
19
44
|
```js
|
|
20
45
|
{
|
|
@@ -26,12 +51,15 @@ Read `.gsd-t/progress.md` and each domain's `scope.md`/`constraints.md`. The par
|
|
|
26
51
|
phase: "plan",
|
|
27
52
|
milestone: "M{NN}",
|
|
28
53
|
projectDir: ".",
|
|
29
|
-
userInput: "$ARGUMENTS"
|
|
54
|
+
userInput: "$ARGUMENTS",
|
|
55
|
+
// M86: inject the resolved overrides map (pre-mortem uses this).
|
|
56
|
+
// Pass {} when the resolver failed AND you chose the loud-warning path (not halt).
|
|
57
|
+
overrides: { /* ...from resolver result.overrides, or {} on failure */ }
|
|
30
58
|
}
|
|
31
59
|
}
|
|
32
60
|
```
|
|
33
61
|
|
|
34
|
-
## Step
|
|
62
|
+
## Step 4: Interpret the result
|
|
35
63
|
|
|
36
64
|
The Workflow returns `{ status, artifacts, summary, decisions, traceability?, preMortem? }`.
|
|
37
65
|
|
package/commands/gsd-t-prd.md
CHANGED
|
@@ -14,7 +14,32 @@ The agent takes a user's idea — however rough — reads available GSD-T projec
|
|
|
14
14
|
|
|
15
15
|
Read any existing `docs/requirements.md`, `docs/architecture.md`, and `.gsd-t/progress.md`. Capture the user's idea from `$ARGUMENTS`.
|
|
16
16
|
|
|
17
|
-
## Step 2:
|
|
17
|
+
## Step 2: Resolve the active model profile (M86 — invoke-time injection)
|
|
18
|
+
|
|
19
|
+
Before calling the Workflow, resolve the active model profile to build the `overrides` map:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
# Run via Bash at invoke time:
|
|
23
|
+
gsd-t model-profile resolve --json
|
|
24
|
+
# Bare form (NO --profile flag): reads .gsd-t/model-profile.json — profile AND stageOverrides
|
|
25
|
+
# (set-stage overrides MUST win — contract precedence; --profile is a config-blind diagnostic
|
|
26
|
+
# form that ZEROES stageOverrides and must never be used for invocation — Red Team M86 r3)
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
**Resolver-failure handling (M86 — pre-mortem c2 #2):** if the resolve call fails, do NOT
|
|
30
|
+
silently proceed on the premium fallback. Either HALT with `blocked-needs-human`, or proceed
|
|
31
|
+
ONLY with a loud, surfaced warning:
|
|
32
|
+
```
|
|
33
|
+
⚠ model-profile resolver unavailable — running on PREMIUM fallback literals
|
|
34
|
+
(configured profile unknown; stale global binary may lack model-profile subcommand)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Also surface a SUCCESSFUL resolve that carries a `configError` field (the resolver returns a
|
|
38
|
+
named default + `configError` for malformed/hand-edited configs — Red Team M86): print the
|
|
39
|
+
`configError` as a visible warning naming the effective profile before proceeding. A clean-looking
|
|
40
|
+
run on a posture the user did not configure is the same silent-spend failure class.
|
|
41
|
+
|
|
42
|
+
## Step 3: Invoke the phase Workflow
|
|
18
43
|
|
|
19
44
|
```js
|
|
20
45
|
{
|
|
@@ -25,12 +50,15 @@ Read any existing `docs/requirements.md`, `docs/architecture.md`, and `.gsd-t/pr
|
|
|
25
50
|
args: {
|
|
26
51
|
phase: "prd",
|
|
27
52
|
projectDir: ".",
|
|
28
|
-
userInput: "$ARGUMENTS"
|
|
53
|
+
userInput: "$ARGUMENTS",
|
|
54
|
+
// M86: inject the resolved overrides map.
|
|
55
|
+
// Pass {} when the resolver failed AND you chose the loud-warning path (not halt).
|
|
56
|
+
overrides: { /* ...from resolver result.overrides, or {} on failure */ }
|
|
29
57
|
}
|
|
30
58
|
}
|
|
31
59
|
```
|
|
32
60
|
|
|
33
|
-
## Step
|
|
61
|
+
## Step 4: Interpret the result
|
|
34
62
|
|
|
35
63
|
The Workflow returns `{ status, artifacts, summary, decisions }`.
|
|
36
64
|
|
package/commands/gsd-t-status.md
CHANGED
|
@@ -54,6 +54,7 @@ Present a concise status to the user:
|
|
|
54
54
|
```
|
|
55
55
|
📊 GSD-T Status: {milestone name}
|
|
56
56
|
Phase: {PARTITIONED | DISCUSSED | PLANNED | EXECUTING | INTEGRATED | VERIFIED}
|
|
57
|
+
Model Profile: {profile-name} [{(default)} if no per-project config set]
|
|
57
58
|
|
|
58
59
|
Domains:
|
|
59
60
|
{domain-1}: {completed}/{total} tasks {✅ done | 🔄 in progress | ⏳ blocked}
|
|
@@ -72,6 +73,19 @@ Recent decisions:
|
|
|
72
73
|
- {latest decision from Decision Log}
|
|
73
74
|
```
|
|
74
75
|
|
|
76
|
+
### Active Model Profile Line (M86 — SC(f): no silent degradation)
|
|
77
|
+
|
|
78
|
+
The **Model Profile** line MUST always name the active profile — never blank, never an implicit fallback.
|
|
79
|
+
|
|
80
|
+
- Read `.gsd-t/model-profile.json` in the project root. If the file exists and contains a valid `profile` field (`standard` | `pro` | `premium`), display it by name: `Model Profile: pro`.
|
|
81
|
+
- If the file is absent, display the global default by name with the `(default)` marker: `Model Profile: premium (default)`.
|
|
82
|
+
- If the file is present but malformed or contains an unknown profile, display: `Model Profile: premium (default, config-error)` — never silently promote to the most expensive posture.
|
|
83
|
+
|
|
84
|
+
Profiles control which workflow stages run on Fable vs. Opus/Sonnet:
|
|
85
|
+
- `standard` — zero Fable stages (pre-M85 posture)
|
|
86
|
+
- `pro` — Fable on red-team + pre-mortem + debug-cycle-2
|
|
87
|
+
- `premium` — all 6 M85 designated Fable stages (full posture, global default)
|
|
88
|
+
|
|
75
89
|
### Backlog Section
|
|
76
90
|
|
|
77
91
|
If `.gsd-t/backlog.md` exists, read and parse it. Show total count and top 3 items (position, title, type). If no backlog file exists, skip the Backlog section entirely. If the backlog file exists but is empty (no entries), show `Backlog: No items`.
|
package/commands/gsd-t-verify.md
CHANGED
|
@@ -28,7 +28,36 @@ Per `.gsd-t/contracts/orthogonal-validation-contract.md` v1.0.0 STABLE, the thre
|
|
|
28
28
|
|
|
29
29
|
Read `.gsd-t/progress.md` to determine the active milestone.
|
|
30
30
|
|
|
31
|
-
## Step 2:
|
|
31
|
+
## Step 2: Resolve the active model profile (M86 — invoke-time injection)
|
|
32
|
+
|
|
33
|
+
Before calling the Workflow, resolve the active model profile to build the `overrides` map:
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
# Run via Bash at invoke time:
|
|
37
|
+
gsd-t model-profile resolve --json
|
|
38
|
+
# Bare form (NO --profile flag): reads .gsd-t/model-profile.json — profile AND stageOverrides
|
|
39
|
+
# (set-stage overrides MUST win — contract precedence; --profile is a config-blind diagnostic
|
|
40
|
+
# form that ZEROES stageOverrides and must never be used for invocation — Red Team M86 r3)
|
|
41
|
+
# Output: { "ok": true, "profile": "...", "overrides": { "stage-key": "concrete-model-id", ... } }
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
**Resolver-failure handling (M86 — SC(f), pre-mortem c2 #2):** if the resolve call fails
|
|
45
|
+
(`{ok:false}`, spawn error, or the `model-profile` subcommand is not present in the installed
|
|
46
|
+
binary), do NOT silently proceed on the premium fallback. Either:
|
|
47
|
+
- HALT with `blocked-needs-human` and explain the resolver is unavailable; OR
|
|
48
|
+
- Proceed ONLY with a **loud, surfaced warning** that names the effective posture:
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
⚠ model-profile resolver unavailable — running on PREMIUM fallback literals
|
|
52
|
+
(configured profile unknown; stale global binary may lack model-profile subcommand)
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Also surface a SUCCESSFUL resolve that carries a `configError` field (the resolver returns a
|
|
56
|
+
named default + `configError` for malformed/hand-edited configs — Red Team M86): print the
|
|
57
|
+
`configError` as a visible warning naming the effective profile before proceeding. A clean-looking
|
|
58
|
+
run on a posture the user did not configure is the same silent-spend failure class.
|
|
59
|
+
|
|
60
|
+
## Step 3: Invoke the verify Workflow
|
|
32
61
|
|
|
33
62
|
Call the `Workflow` tool with:
|
|
34
63
|
|
|
@@ -41,6 +70,10 @@ Call the `Workflow` tool with:
|
|
|
41
70
|
args: {
|
|
42
71
|
milestone: "M{NN}",
|
|
43
72
|
projectDir: ".",
|
|
73
|
+
// M86: inject the resolved overrides map so the workflow's ?? forms (including
|
|
74
|
+
// red-team) pick up the profile-tier assignments instead of premium literals.
|
|
75
|
+
// Pass {} when the resolver failed AND you chose the loud-warning path (not halt).
|
|
76
|
+
overrides: { /* ...from resolver result.overrides, or {} on failure */ },
|
|
44
77
|
// Optional: skip /code-review ultra when rate-limited.
|
|
45
78
|
// skipUltra: false,
|
|
46
79
|
// skipUltraReason: "ultra rate-limited per error E429",
|
|
@@ -50,7 +83,7 @@ Call the `Workflow` tool with:
|
|
|
50
83
|
|
|
51
84
|
`skipUltra: true` requires `skipUltraReason: string` per contract Rule #2. A run with `skipUltra: true` is INELIGIBLE for `VERIFIED` — best attainable verdict is `VERIFIED-WITH-WARNINGS`. Red Team and QA are non-skippable.
|
|
52
85
|
|
|
53
|
-
## Step
|
|
86
|
+
## Step 4: Interpret the result
|
|
54
87
|
|
|
55
88
|
The Workflow returns:
|
|
56
89
|
|
package/commands/gsd-t-wave.md
CHANGED
|
@@ -23,7 +23,35 @@ The native Workflow `workflow()` global runs each sub-workflow inline and return
|
|
|
23
23
|
|
|
24
24
|
Read `.gsd-t/progress.md` to determine the active milestone and the wave domain list from `.gsd-t/contracts/m{NN}-integration-points.md`.
|
|
25
25
|
|
|
26
|
-
## Step 2:
|
|
26
|
+
## Step 2: Resolve the active model profile (M86 — invoke-time injection)
|
|
27
|
+
|
|
28
|
+
Before calling the Workflow, resolve the active model profile to build the `overrides` map:
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Run via Bash at invoke time:
|
|
32
|
+
gsd-t model-profile resolve --json
|
|
33
|
+
# Bare form (NO --profile flag): reads .gsd-t/model-profile.json — profile AND stageOverrides
|
|
34
|
+
# (set-stage overrides MUST win — contract precedence; --profile is a config-blind diagnostic
|
|
35
|
+
# form that ZEROES stageOverrides and must never be used for invocation — Red Team M86 r3)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
**Resolver-failure handling (M86 — pre-mortem c2 #2):** if the resolve call fails, do NOT
|
|
39
|
+
silently proceed on the premium fallback. Either HALT with `blocked-needs-human`, or proceed
|
|
40
|
+
ONLY with a loud, surfaced warning:
|
|
41
|
+
```
|
|
42
|
+
⚠ model-profile resolver unavailable — running on PREMIUM fallback literals
|
|
43
|
+
(configured profile unknown; stale global binary may lack model-profile subcommand)
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Also surface a SUCCESSFUL resolve that carries a `configError` field (the resolver returns a
|
|
47
|
+
named default + `configError` for malformed/hand-edited configs — Red Team M86): print the
|
|
48
|
+
`configError` as a visible warning naming the effective profile before proceeding. A clean-looking
|
|
49
|
+
run on a posture the user did not configure is the same silent-spend failure class.
|
|
50
|
+
|
|
51
|
+
The wave workflow forwards `overrides` to BOTH its `gsd-t-execute` and `gsd-t-verify`
|
|
52
|
+
sub-workflow calls, so the spend switch is active across the full cycle (execute + verify).
|
|
53
|
+
|
|
54
|
+
## Step 3: Invoke the wave Workflow
|
|
27
55
|
|
|
28
56
|
Call the `Workflow` tool with:
|
|
29
57
|
|
|
@@ -36,12 +64,15 @@ Call the `Workflow` tool with:
|
|
|
36
64
|
args: {
|
|
37
65
|
milestone: "M{NN}",
|
|
38
66
|
domains: ["m{NN}-d1-...", "m{NN}-d2-...", ...], // domains for this wave only, per integration-points
|
|
39
|
-
projectDir: "."
|
|
67
|
+
projectDir: ".",
|
|
68
|
+
// M86: inject the resolved overrides map — forwarded to execute + verify sub-workflows.
|
|
69
|
+
// Pass {} when the resolver failed AND you chose the loud-warning path (not halt).
|
|
70
|
+
overrides: { /* ...from resolver result.overrides, or {} on failure */ }
|
|
40
71
|
}
|
|
41
72
|
}
|
|
42
73
|
```
|
|
43
74
|
|
|
44
|
-
## Step
|
|
75
|
+
## Step 4: Interpret the result
|
|
45
76
|
|
|
46
77
|
```js
|
|
47
78
|
{
|
package/docs/requirements.md
CHANGED
|
@@ -977,6 +977,12 @@ Supporting contracts (to be written during D1):
|
|
|
977
977
|
| REQ-M79-02 | `genSystemArchitecture` draws up to 12 real domains from scan data with rounded classDefs. All 5 diagrams rendered with shared `MERMAID_CONFIG` (dark base theme + rounded corners + node padding/spacing) applied via `mmdc -c`, plus `-b transparent`. The Database Schema diagram is suppressed by default (`SUPPRESSED_TYPES`, opt-in via `includeSchemaDiagram` option) because Drizzle schema parsing produces inaccurate results on large repos. | m79-d1 | T3 | complete |
|
|
978
978
|
| REQ-M79-03 | `genSequence` uses `"validate and sanitize"` (unquoted `&` broke the Mermaid sequence parser). `scan.test.js` and `verify-gates.js` reflect the 5-diagrams-by-default contract (schema diagram excluded unless opted-in). | m79-d1 | T4 | complete |
|
|
979
979
|
| REQ-M79-VERIFY | +1 regression test (m79-diagram-quality). Suite 1325/1325 pass. Hilo report regenerated: 5/5 diagrams render, 35 real services, rounded corners present, schema section gone, no placeholders. Patch bump 4.0.26 → 4.0.27. | m79-d1 | T5 | complete |
|
|
980
|
+
| REQ-M86-01 | Three model profiles (standard/pro/premium) as a second dimension over the M85 `STAGE_TIERS` — `PROFILE_STAGE_TIERS` + `resolveProfile` on `bin/gsd-t-model-tier-policy.cjs` (standard = zero fable; pro = red-team + pre-mortem + debug-cycle-2 fable; premium = all 6; producers HELD opus in every profile — M82). Precedence `stageOverrides[stage] ?? profile-tier ?? named global default`, blindness clamps at write AND resolve level, own-property validation (prototype-key bypass killed). | m86-d1 | T1–T5 | complete (M86, v4.5.10) |
|
|
981
|
+
| REQ-M86-02 | Per-project config `.gsd-t/model-profile.json` + `gsd-t model-profile` CLI (show/set/set-stage/resolve/--json). Malformed/hand-edited configs produce defined envelopes with explicit configError — never a silent clean-premium fall-through. set-stage refuses to rewrite erroring configs. | m86-d1 | T2/T5 | complete (M86) |
|
|
982
|
+
| REQ-M86-03 | Invoke-time injection (M69): ALL 10 workflow-invoking commands resolve the active config via the bare `resolve --json` form (stageOverrides WIN) and inject `overrides` into workflow args; designated stages read `model: overrides["<stage>"] ?? "<premium-literal>"`; wave forwards overrides to both sub-workflow calls; resolver-failure = halt or loud named-posture warning, never silent premium. NO tracked-file mutation on profile switch. | m86-d2 | T1–T10 | complete (M86) |
|
|
983
|
+
| REQ-M86-04 | Drift lint unwraps the `??` form: validates bracket key against the 6 INJECTABLE stages (producers excluded), fallback literal against tier set + stage policy, flat AND combined-debug forms, fail-closed; 8 mandatory negatives incl. typo'd key + wrapped-producers + drifted combined-form. Invoker fleet lint pins the injection block + failure clause + configError surfacing + bare resolve form. | m86-d3 (+d2 T10) | lint suites | complete (M86) |
|
|
984
|
+
| REQ-M86-05 | Active profile surfaced NAMED at every surface (SC f): `[GSD-T PROFILE]` banner token (GSD-T projects only), statusline segment, `gsd-t status` line, CLI envelopes — global default named when config absent; hook/statusline resilient to resolver absence/errors (exit 0, `[GSD-T NOW]` intact). | m86-d4 | m86-surfacing | complete (M86) |
|
|
985
|
+
| REQ-M86-06 | Installer self-protection: `copyBinToolsToProject` skips the GSD-T source repo (copy loop AND stray sweep) — propagation must never revert in-flight source work; CLI carries a version-skew guard (structured error on pre-M86 sibling module). | m86-d1 (verify fix) | r2 killing tests | complete (M86) |
|
|
980
986
|
|
|
981
987
|
## Updated Functional Requirements (scan findings - v4.0.27)
|
|
982
988
|
|
|
@@ -1037,5 +1043,6 @@ The deep scan identified functional deficiencies not captured in previous requir
|
|
|
1037
1043
|
| REQ-M77 | test/m77-renderer-table-summary.test.js | 4 | passing |
|
|
1038
1044
|
| REQ-M78 | test/m78-plain-english-grouping.test.js | 3 | passing |
|
|
1039
1045
|
| REQ-M79 | test/m79-diagram-quality.test.js | 1 | passing |
|
|
1046
|
+
| REQ-M86-* | test/m86-policy-profiles.test.js, test/m86-invoker-injection.test.js, test/m86-lint-unwrap-fallback.test.js, test/m86-surfacing.test.js, test/m85-workflow-tier-policy-lint.test.js (extended) | 146 | passing |
|
|
1040
1047
|
|
|
1041
|
-
**Total automated tests (v4.
|
|
1048
|
+
**Total automated tests (v4.5.10)**: 1598 pass / 0 fail / 4 skip. Runner: `node --test` (zero dependencies). E2E: `playwright.config.ts` at project root, `e2e/` directory with journey, viewer, and live-journey specs.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tekyzinc/gsd-t",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.6.11",
|
|
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",
|
|
@@ -10,13 +10,90 @@
|
|
|
10
10
|
* dated banner at the top of Claude's response. Fresh per turn so multi-day
|
|
11
11
|
* sessions and date-rollovers are reflected accurately.
|
|
12
12
|
*
|
|
13
|
-
* Conditionally emits (GSD-T projects only
|
|
14
|
-
* - [GSD-T
|
|
13
|
+
* Conditionally emits (GSD-T projects only — dirs with .gsd-t/):
|
|
14
|
+
* - [GSD-T PROFILE] {profile} — active model profile (M86). Gated like the
|
|
15
|
+
* statusline segment: a model profile is a GSD-T concept; announcing
|
|
16
|
+
* "premium (default)" in unrelated directories is noise (Red Team M86 LOW).
|
|
17
|
+
* - [GSD-T AUTO-ROUTE] signal so Claude routes via /gsd (plain text prompts
|
|
18
|
+
* in projects with .gsd-t/progress.md only)
|
|
19
|
+
*
|
|
20
|
+
* NOTE: resolveActiveProfile duplicates the read/validate logic of
|
|
21
|
+
* readConfig() in bin/gsd-t-model-profile.cjs (the canonical copy) — kept
|
|
22
|
+
* inline because this hook must be zero-dep and runs from ~/.claude/scripts
|
|
23
|
+
* where the package module may be absent. Keep the two in sync.
|
|
15
24
|
*/
|
|
16
25
|
|
|
17
26
|
const fs = require("fs");
|
|
18
27
|
const path = require("path");
|
|
19
28
|
|
|
29
|
+
/**
|
|
30
|
+
* Resolve the active model profile from .gsd-t/model-profile.json.
|
|
31
|
+
*
|
|
32
|
+
* Resilience contract (pre-mortem r1 #7 MEDIUM — hook resilience):
|
|
33
|
+
* - Resolver module/binary absent → named global default with (default) marker.
|
|
34
|
+
* - Resolver error / malformed config → named global default or "unknown" marker.
|
|
35
|
+
* - NEVER throws, NEVER suppresses the [GSD-T NOW] line, NEVER kills auto-routing.
|
|
36
|
+
*
|
|
37
|
+
* @param {string} cwd — project root (may be any dir; absent .gsd-t is fine)
|
|
38
|
+
* @returns {{ profile: string, isDefault: boolean, configError?: string }}
|
|
39
|
+
*/
|
|
40
|
+
function resolveActiveProfile(cwd) {
|
|
41
|
+
const GLOBAL_DEFAULT = 'premium';
|
|
42
|
+
const VALID_PROFILES = ['standard', 'pro', 'premium'];
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const configPath = path.join(cwd, '.gsd-t', 'model-profile.json');
|
|
46
|
+
if (!fs.existsSync(configPath)) {
|
|
47
|
+
return { profile: GLOBAL_DEFAULT, isDefault: true };
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let raw;
|
|
51
|
+
try {
|
|
52
|
+
raw = fs.readFileSync(configPath, 'utf8');
|
|
53
|
+
} catch (_) {
|
|
54
|
+
return { profile: GLOBAL_DEFAULT, isDefault: true, configError: 'unreadable' };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
let config;
|
|
58
|
+
try {
|
|
59
|
+
config = JSON.parse(raw);
|
|
60
|
+
} catch (_) {
|
|
61
|
+
return { profile: GLOBAL_DEFAULT, isDefault: true, configError: 'invalid-json' };
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!config || typeof config !== 'object' || Array.isArray(config)) {
|
|
65
|
+
return { profile: GLOBAL_DEFAULT, isDefault: true, configError: 'wrong-type' };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const p = config.profile;
|
|
69
|
+
if (typeof p !== 'string' || !VALID_PROFILES.includes(p)) {
|
|
70
|
+
return { profile: GLOBAL_DEFAULT, isDefault: true, configError: 'unknown-profile' };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
return { profile: p, isDefault: false };
|
|
74
|
+
} catch (_) {
|
|
75
|
+
// Catch-all: never crash the hook
|
|
76
|
+
return { profile: GLOBAL_DEFAULT, isDefault: true };
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Format the profile token for the banner.
|
|
82
|
+
* SC(f): always named — never blank, never an unsurfaced fallback.
|
|
83
|
+
*/
|
|
84
|
+
function profileToken(profileResult) {
|
|
85
|
+
if (!profileResult || typeof profileResult.profile !== 'string') {
|
|
86
|
+
return 'profile: unknown';
|
|
87
|
+
}
|
|
88
|
+
if (profileResult.configError) {
|
|
89
|
+
return `profile: ${profileResult.profile} (default, config-error: ${profileResult.configError})`;
|
|
90
|
+
}
|
|
91
|
+
if (profileResult.isDefault) {
|
|
92
|
+
return `profile: ${profileResult.profile} (default)`;
|
|
93
|
+
}
|
|
94
|
+
return `profile: ${profileResult.profile}`;
|
|
95
|
+
}
|
|
96
|
+
|
|
20
97
|
function liveTimestamp(now = new Date()) {
|
|
21
98
|
const day = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"][now.getDay()];
|
|
22
99
|
const mon = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
|
@@ -32,17 +109,40 @@ function liveTimestamp(now = new Date()) {
|
|
|
32
109
|
return `${date} ${time}${tzShort ? " " + tzShort : ""}`;
|
|
33
110
|
}
|
|
34
111
|
|
|
112
|
+
// Hook runtime — guarded so require()-ing this module for unit tests
|
|
113
|
+
// (test/m86-surfacing.test.js) does NOT attach stdin listeners. An unguarded
|
|
114
|
+
// stdin listener holds the requiring test process's event loop open forever
|
|
115
|
+
// (npm test runner children get a never-EOF stdin pipe → suite hang), and the
|
|
116
|
+
// "end" handler's process.exit(0) would kill the test process on stdin EOF.
|
|
117
|
+
if (require.main === module) {
|
|
35
118
|
let input = "";
|
|
36
119
|
process.stdin.setEncoding("utf8");
|
|
37
120
|
process.stdin.on("data", (chunk) => { input += chunk; });
|
|
38
121
|
process.stdin.on("end", () => {
|
|
39
122
|
// Always emit live timestamp first — every turn, every project.
|
|
123
|
+
// [GSD-T NOW] format is date-guard-invariant: NEVER alter it.
|
|
40
124
|
process.stdout.write(`[GSD-T NOW] ${liveTimestamp()}\n`);
|
|
41
125
|
|
|
126
|
+
// Parse stdin ONCE; both the profile token and auto-route reuse it.
|
|
127
|
+
let data = null;
|
|
128
|
+
try { data = JSON.parse(input); } catch (_) { /* tolerated — gates below skip */ }
|
|
129
|
+
const cwd = (data && typeof data.cwd === "string" && data.cwd) ? data.cwd : process.cwd();
|
|
130
|
+
|
|
131
|
+
// Emit the active model profile — GSD-T projects only (dirs with .gsd-t/),
|
|
132
|
+
// matching the statusline gate. SC(f): named, never blank, never a crash.
|
|
133
|
+
try {
|
|
134
|
+
if (fs.existsSync(path.join(cwd, ".gsd-t"))) {
|
|
135
|
+
const profileResult = resolveActiveProfile(cwd);
|
|
136
|
+
process.stdout.write(`[GSD-T PROFILE] ${profileToken(profileResult)}\n`);
|
|
137
|
+
}
|
|
138
|
+
} catch (_) {
|
|
139
|
+
// Belt-and-suspenders: if the gate or profileToken throws, emit the unknown marker.
|
|
140
|
+
process.stdout.write(`[GSD-T PROFILE] profile: unknown\n`);
|
|
141
|
+
}
|
|
142
|
+
|
|
42
143
|
try {
|
|
43
|
-
const data = JSON.parse(input);
|
|
44
144
|
// Auto-route is GSD-T-project-only.
|
|
45
|
-
|
|
145
|
+
if (!data) process.exit(0);
|
|
46
146
|
if (!fs.existsSync(path.join(cwd, ".gsd-t", "progress.md"))) process.exit(0);
|
|
47
147
|
const prompt = (typeof data.prompt === "string" ? data.prompt : "").trimStart();
|
|
48
148
|
if (prompt.startsWith("/")) process.exit(0); // slash command — pass through
|
|
@@ -58,5 +158,6 @@ process.stdin.on("end", () => {
|
|
|
58
158
|
}
|
|
59
159
|
process.exit(0);
|
|
60
160
|
});
|
|
161
|
+
}
|
|
61
162
|
|
|
62
|
-
module.exports = { liveTimestamp };
|
|
163
|
+
module.exports = { liveTimestamp, resolveActiveProfile, profileToken };
|
|
@@ -12,6 +12,11 @@
|
|
|
12
12
|
// never populated those env vars. When the state file is absent or stale
|
|
13
13
|
// (>5min), the context segment is omitted.
|
|
14
14
|
//
|
|
15
|
+
// Active model profile is read from .gsd-t/model-profile.json (M86 — SC(f):
|
|
16
|
+
// always named, never blank). When the file is absent, the named global default
|
|
17
|
+
// (premium) is shown with a (default) marker. Resilient: never crashes on missing
|
|
18
|
+
// or malformed config (propagation-gap class — pre-mortem c2 #5).
|
|
19
|
+
//
|
|
15
20
|
// Zero external dependencies.
|
|
16
21
|
|
|
17
22
|
const fs = require('fs');
|
|
@@ -26,6 +31,53 @@ const DIM = '\x1b[2m';
|
|
|
26
31
|
const BOLD = '\x1b[1m';
|
|
27
32
|
const ORANGE = '\x1b[38;5;208m'; // 256-color orange
|
|
28
33
|
|
|
34
|
+
// ─── Profile resolution (M86 — SC(f): always named, never blank) ─────────────
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Resolve the active model profile from .gsd-t/model-profile.json.
|
|
38
|
+
*
|
|
39
|
+
* Resilience contract (pre-mortem c2 #5 — statusline resilience):
|
|
40
|
+
* - Config absent → named global default with (default) marker.
|
|
41
|
+
* - Malformed config / parse error → named global default or configError marker.
|
|
42
|
+
* - NEVER throws, NEVER crashes the statusline.
|
|
43
|
+
*
|
|
44
|
+
* @param {string|null} projectRoot — resolved project root (may be null)
|
|
45
|
+
* @returns {{ profile: string, isDefault: boolean, configError?: string }|null}
|
|
46
|
+
* null when projectRoot is null (non-GSD-T directory — omit segment).
|
|
47
|
+
*/
|
|
48
|
+
// NOTE: duplicates the read/validate logic of readConfig() in bin/gsd-t-model-profile.cjs
|
|
49
|
+
// (the canonical copy) — kept inline because the statusline must be zero-dep and runs from
|
|
50
|
+
// ~/.claude/scripts where the package module may be absent. Keep the copies in sync.
|
|
51
|
+
function readActiveProfile(projectRoot) {
|
|
52
|
+
if (!projectRoot) return null;
|
|
53
|
+
const GLOBAL_DEFAULT = 'premium';
|
|
54
|
+
const VALID_PROFILES = ['standard', 'pro', 'premium'];
|
|
55
|
+
try {
|
|
56
|
+
const configPath = path.join(projectRoot, '.gsd-t', 'model-profile.json');
|
|
57
|
+
if (!fs.existsSync(configPath)) {
|
|
58
|
+
return { profile: GLOBAL_DEFAULT, isDefault: true };
|
|
59
|
+
}
|
|
60
|
+
let raw;
|
|
61
|
+
try { raw = fs.readFileSync(configPath, 'utf8'); } catch (_) {
|
|
62
|
+
return { profile: GLOBAL_DEFAULT, isDefault: true, configError: 'unreadable' };
|
|
63
|
+
}
|
|
64
|
+
let config;
|
|
65
|
+
try { config = JSON.parse(raw); } catch (_) {
|
|
66
|
+
return { profile: GLOBAL_DEFAULT, isDefault: true, configError: 'invalid-json' };
|
|
67
|
+
}
|
|
68
|
+
if (!config || typeof config !== 'object' || Array.isArray(config)) {
|
|
69
|
+
return { profile: GLOBAL_DEFAULT, isDefault: true, configError: 'wrong-type' };
|
|
70
|
+
}
|
|
71
|
+
const p = config.profile;
|
|
72
|
+
if (typeof p !== 'string' || !VALID_PROFILES.includes(p)) {
|
|
73
|
+
return { profile: GLOBAL_DEFAULT, isDefault: true, configError: 'unknown-profile' };
|
|
74
|
+
}
|
|
75
|
+
return { profile: p, isDefault: false };
|
|
76
|
+
} catch (_) {
|
|
77
|
+
return { profile: GLOBAL_DEFAULT, isDefault: true };
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
29
81
|
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
30
82
|
|
|
31
83
|
function findProjectRoot() {
|
|
@@ -105,5 +157,24 @@ if (ctxPct !== null) {
|
|
|
105
157
|
contextPart = ` │ ctx ${contextBar(ctxPct)}`;
|
|
106
158
|
}
|
|
107
159
|
|
|
108
|
-
|
|
160
|
+
// Active model profile (M86 — SC(f): always named when in a GSD-T project)
|
|
161
|
+
let profilePart = '';
|
|
162
|
+
try {
|
|
163
|
+
const profileResult = readActiveProfile(root);
|
|
164
|
+
if (profileResult) {
|
|
165
|
+
let label;
|
|
166
|
+
if (profileResult.configError) {
|
|
167
|
+
label = `${profileResult.profile}* (default)`; // * signals config error
|
|
168
|
+
} else if (profileResult.isDefault) {
|
|
169
|
+
label = `${profileResult.profile} (default)`;
|
|
170
|
+
} else {
|
|
171
|
+
label = profileResult.profile;
|
|
172
|
+
}
|
|
173
|
+
profilePart = ` │ ${DIM}profile:${RESET} ${label}`;
|
|
174
|
+
}
|
|
175
|
+
} catch (_) {
|
|
176
|
+
// Resilience: never crash the statusline
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const line = `gsd-t │ ${projectPart}${profilePart}${contextPart}`;
|
|
109
180
|
process.stdout.write(line + '\n');
|