@tekyzinc/gsd-t 4.3.10 → 4.4.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 +16 -0
- package/README.md +5 -4
- package/bin/gsd-t-model-tier-policy.cjs +168 -0
- package/bin/gsd-t-parallel.cjs +17 -7
- package/bin/gsd-t.js +15 -0
- package/bin/model-selector.js +13 -3
- package/commands/gsd-t-help.md +7 -0
- package/package.json +1 -1
- package/scripts/hooks/gsd-t-ctx-cue.sh +58 -0
- package/scripts/statusline-command.sh +119 -0
- package/templates/CLAUDE-global.md +4 -3
- package/templates/workflows/gsd-t-debug.workflow.js +1 -1
- package/templates/workflows/gsd-t-phase.workflow.js +4 -4
- package/templates/workflows/gsd-t-verify.workflow.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to GSD-T are documented here. Updated with each release.
|
|
4
4
|
|
|
5
|
+
## [4.4.10] - 2026-06-09 (M85 Model-Tier Policy + Fable 5 — minor)
|
|
6
|
+
|
|
7
|
+
### Added — single source of truth for model-tier assignments + the Fable 5 tier
|
|
8
|
+
|
|
9
|
+
Model-tier policy previously lived in 4 unsynced authorities with zero drift enforcement, and the parallel alias map was provably stale (`opus → claude-opus-4-7`). M85 centralizes the policy, fixes the live bug, and slots Claude Fable 5 (tier above Opus, $10/$50 per MTok) into the highest-leverage stages — gated by a lint so drift is impossible. The cost tradeoff was MEASURED, not asserted: a Fable single-draft tied a judged 3-Opus competition at 42% of the cost (n=1, discuss-class).
|
|
10
|
+
|
|
11
|
+
- `bin/gsd-t-model-tier-policy.cjs`: NEW — frozen `MODEL_IDS` + `STAGE_TIERS` (6 fable stage keys; competition producers HELD at opus per the M82 blindness invariant), `requiresThinkingOmitted()` (Fable's thinking-disabled-400 breaking change encoded once; accepts the runtime bracket-suffix form), `resolve()` + CLI resolver emitting the M69 JSON envelope; `gsd-t model-tier-policy` dispatcher + registered in both bin-propagation lists.
|
|
12
|
+
- `bin/gsd-t-parallel.cjs`: alias map now `require()`s the policy module (zero bare model-id literals; stale opus-4-7 gone); cache-warm probe passes `--model` explicitly (the `ANTHROPIC_MODEL` env pin was measured silently ignored by the current CLI).
|
|
13
|
+
- `bin/model-selector.js`: FABLE tier + `cycle_2_escalation` rule via the existing `selectModel` signature; debug default byte-identical.
|
|
14
|
+
- `templates/workflows/gsd-t-{phase,verify,debug}.workflow.js`: 5 Fable assignments — M84 solution-space/partition probes, competition judge (`judge:rubric`), M83 pre-mortem, Red Team (stays non-skippable), debug `cycle === 1 ? "opus" : "fable"` ternary.
|
|
15
|
+
- `test/m85-workflow-tier-policy-lint.test.js`: NEW M71-family drift enforcer — 8-file discovery, stage-key→label mapping with per-stage non-empty-match, negative drift fixtures, real-file + debug-ternary meta-tests.
|
|
16
|
+
- `test/m85-model-tier-policy.test.js` + `test/model-selector.test.js`: 25 + 57 tests incl. dispatcher/propagation killing tests.
|
|
17
|
+
- Contracts: `model-tier-policy-contract.md` v1.0.0 STABLE (new); `model-selection-contract.md` → v1.1.0.
|
|
18
|
+
|
|
19
|
+
No migration needed for consumer projects: workflows keep using tier aliases; `gsd-t update-all` propagates the new module. Suite 1462/0.
|
|
20
|
+
|
|
5
21
|
## [4.3.10] - 2026-06-05 (M84 Auto-Competition - minor)
|
|
6
22
|
|
|
7
23
|
### Changed - Competition Mode is now AUTOMATIC (was opt-in)
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# GSD-T: Contract-Driven Development for Claude Code
|
|
2
2
|
|
|
3
|
-
**v4.
|
|
3
|
+
**v4.4.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.
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
**Rigorous User-Journey Coverage + Anti-Drift Test Quality** — `bin/journey-coverage.cjs` regex listener detector + `gsd-t check-coverage` CLI + `scripts/hooks/pre-commit-journey-coverage` commit gate blocks viewer-source commits when uncovered listeners exist. Journey specs in `e2e/journeys/` use functional assertions (zero `toBeVisible`-only tests) per the E2E Test Quality Standard in CLAUDE.md.
|
|
19
19
|
**Universal Playwright Bootstrap + Deterministic UI Enforcement (M50)** — three executable enforcement layers: (1) `bin/playwright-bootstrap.cjs` + `bin/ui-detection.cjs` - idempotent installer detects package manager, installs `@playwright/test` + chromium, scaffolds `e2e/`; (2) Workflow runtime runs `playwright-bootstrap.cjs::installPlaywright()` before any E2E stage when `hasUI && !hasPlaywright`; install failure halts with `blocked-needs-human`; (3) `scripts/hooks/pre-commit-playwright-gate` (opt-in via `gsd-t doctor --install-hooks`) blocks viewer-source commits when staged files are newer than `.gsd-t/.last-playwright-pass`. The `gsd-t setup-playwright [path]` subcommand handles manual install.
|
|
20
20
|
**Visualizer (`/gsd-t-visualize`)** — launches a real-time browser dashboard with dual-pane view: top pane streams the main session, bottom pane streams whichever spawn the user clicks. Left rail shows Live Spawns and Completed (last 100 spawns, status-badged, collapsible). Right rail shows Spawn Plan / Parallelism / Tool Cost. Powered by `gsd-t-stream-feed-server.js` + `gsd-t-dashboard.html`.
|
|
21
|
-
**Surgical model selection** — `bin/model-selector.js` assigns haiku/sonnet/opus per phase via a declarative rules table; `/advisor` escalation path with convention-based fallback.
|
|
21
|
+
**Surgical model selection** — `bin/model-selector.js` assigns haiku/sonnet/opus/fable per phase via a declarative rules table; `/advisor` escalation path with convention-based fallback. **M85 single-source tier policy:** `bin/gsd-t-model-tier-policy.cjs` is the SINGLE source of truth for model-tier assignments; the 5 highest-leverage stages (solution-space probe, partition probe, competition judge, pre-mortem, Red Team) run on `fable` (Claude Fable 5, tier above Opus); competition producers stay `opus` (M82 blindness); debug escalates cycle-1→opus, cycle-2→fable. Drift is mechanically enforced by the M71-family lint (`test/m85-workflow-tier-policy-lint.test.js`).
|
|
22
22
|
**Token Telemetry** — `gsd-t-calibration-hook.js` records token usage per spawn to `.gsd-t/token-metrics.jsonl` (18-field rows). `gsd-t-token-aggregator.js` aggregates across tasks for the `/gsd-t-metrics` view. Use the native Claude Code `/context` command for live in-session context percentage.
|
|
23
23
|
**Quality North Star** — projects define a `## Quality North Star` section in CLAUDE.md (1–3 sentences, e.g., "This is a published npm library. Every public API must be intuitive and backward-compatible."). `gsd-t-init` auto-detects preset (library/web-app/cli) from package.json signals; `gsd-t-setup` configures it for existing projects. Subagents read it as a quality lens; absent = silent skip (backward compatible).
|
|
24
24
|
**Design Brief Artifact** — during partition, UI/frontend projects (React, Vue, Svelte, Flutter, Tailwind) automatically get `.gsd-t/contracts/design-brief.md` with color palette, typography, spacing system, component patterns, and tone/voice. Non-UI projects skip silently. User-customized briefs are preserved. Referenced in plan phase for visual consistency.
|
|
@@ -391,7 +391,7 @@ Verify with: `/gsd-t-help`
|
|
|
391
391
|
```
|
|
392
392
|
get-stuff-done-teams/
|
|
393
393
|
├── README.md
|
|
394
|
-
├── package.json # @tekyzinc/gsd-t v4.
|
|
394
|
+
├── package.json # @tekyzinc/gsd-t v4.4.10
|
|
395
395
|
├── LICENSE
|
|
396
396
|
├── bin/ # CLI entry + orchestrators + support modules (52 modules)
|
|
397
397
|
│ ├── gsd-t.js # CLI installer + all subcommands
|
|
@@ -407,7 +407,8 @@ get-stuff-done-teams/
|
|
|
407
407
|
│ ├── graph-*.js # Code graph engine (CGC/Neo4j integration)
|
|
408
408
|
│ ├── journey-coverage.cjs # Listener detector + coverage gap reporting
|
|
409
409
|
│ ├── playwright-bootstrap.cjs # Idempotent Playwright installer
|
|
410
|
-
│ ├── model-selector.js # Phase-to-model assignment (haiku/sonnet/opus)
|
|
410
|
+
│ ├── model-selector.js # Phase-to-model assignment (haiku/sonnet/opus/fable)
|
|
411
|
+
│ ├── gsd-t-model-tier-policy.cjs # M85: single-source tier policy (haiku/sonnet/opus/fable), resolver CLI
|
|
411
412
|
│ ├── rule-engine.js # Declarative failure-pattern rules
|
|
412
413
|
│ ├── patch-lifecycle.js # 5-stage patch candidate→graduated lifecycle
|
|
413
414
|
│ └── metrics-collector.js # Task telemetry + ELO tracking
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gsd-t-model-tier-policy.cjs
|
|
3
|
+
*
|
|
4
|
+
* SINGLE source of truth for GSD-T model-tier policy.
|
|
5
|
+
* Zero external runtime deps — installer-package invariant.
|
|
6
|
+
* No top-level side effects.
|
|
7
|
+
*
|
|
8
|
+
* Contract: .gsd-t/contracts/model-tier-policy-contract.md v1.0.0 STABLE
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
'use strict';
|
|
12
|
+
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Published Model-ID Constants (M85 — authoritative, contract v1.0.0)
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Frozen map: tier alias → concrete model id.
|
|
19
|
+
* Consumers MUST import from here — never re-hardcode these strings.
|
|
20
|
+
*
|
|
21
|
+
* @type {Readonly<{opus: string, fable: string, sonnet: string, haiku: string}>}
|
|
22
|
+
*/
|
|
23
|
+
const MODEL_IDS = Object.freeze({
|
|
24
|
+
opus: 'claude-opus-4-8',
|
|
25
|
+
fable: 'claude-fable-5',
|
|
26
|
+
sonnet: 'claude-sonnet-4-6',
|
|
27
|
+
haiku: 'claude-haiku-4-5-20251001',
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// ---------------------------------------------------------------------------
|
|
31
|
+
// Stage Policy (M85 Fable assignments — contract v1.0.0 § "Stage Policy")
|
|
32
|
+
// ---------------------------------------------------------------------------
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Frozen map: stage key → tier alias.
|
|
36
|
+
* 6 stages → fable; competition-producers held at opus (M82 blindness invariant).
|
|
37
|
+
*
|
|
38
|
+
* @type {Readonly<Record<string, string>>}
|
|
39
|
+
*/
|
|
40
|
+
const STAGE_TIERS = Object.freeze({
|
|
41
|
+
'solution-space-probe': 'fable',
|
|
42
|
+
'partition-probe': 'fable',
|
|
43
|
+
'competition-judge': 'fable',
|
|
44
|
+
'competition-producers': 'opus', // HELD — M82 judge-blindness invariant; do NOT move to fable
|
|
45
|
+
'pre-mortem': 'fable',
|
|
46
|
+
'red-team': 'fable',
|
|
47
|
+
'debug-cycle-2': 'fable',
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// requiresThinkingOmitted predicate (encoding the Fable HTTP-400 breaking change)
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Returns true IFF the model requires the explicit thinking-disabled parameter
|
|
56
|
+
* to be OMITTED from the API call.
|
|
57
|
+
*
|
|
58
|
+
* Rationale (canonical, single home): `claude-fable-5` returns HTTP 400 when
|
|
59
|
+
* the explicit thinking-disabled parameter is sent. The parameter must therefore
|
|
60
|
+
* be OMITTED for Fable. No other file may re-implement or re-state this predicate.
|
|
61
|
+
*
|
|
62
|
+
* @param {string} model — concrete model id or tier alias or any string
|
|
63
|
+
* @returns {boolean}
|
|
64
|
+
*/
|
|
65
|
+
function requiresThinkingOmitted(model) {
|
|
66
|
+
if (typeof model !== 'string') return false;
|
|
67
|
+
// Source the id from MODEL_IDS (single-source — no second literal), and accept
|
|
68
|
+
// the runtime's bracket-suffixed display form (e.g. "claude-fable-5[1m]").
|
|
69
|
+
return model === MODEL_IDS.fable || model.startsWith(MODEL_IDS.fable + '[');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
// resolve(stageKey) → concreteModelId
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Returns the concrete model id for the given stage key, or null for unknown keys.
|
|
78
|
+
* Never throws.
|
|
79
|
+
*
|
|
80
|
+
* @param {string} stageKey
|
|
81
|
+
* @returns {string|null}
|
|
82
|
+
*/
|
|
83
|
+
function resolve(stageKey) {
|
|
84
|
+
try {
|
|
85
|
+
const tier = STAGE_TIERS[stageKey];
|
|
86
|
+
if (!tier) return null;
|
|
87
|
+
const modelId = MODEL_IDS[tier];
|
|
88
|
+
return modelId !== undefined ? modelId : null;
|
|
89
|
+
} catch (_) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
// Exports
|
|
96
|
+
// ---------------------------------------------------------------------------
|
|
97
|
+
|
|
98
|
+
module.exports = {
|
|
99
|
+
MODEL_IDS,
|
|
100
|
+
STAGE_TIERS,
|
|
101
|
+
requiresThinkingOmitted,
|
|
102
|
+
resolve,
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
// CLI dispatch (M69 invoke-time injection surface)
|
|
107
|
+
// run: node bin/gsd-t-model-tier-policy.cjs resolve <stageKey> [--json]
|
|
108
|
+
// ---------------------------------------------------------------------------
|
|
109
|
+
|
|
110
|
+
if (require.main === module) {
|
|
111
|
+
const args = process.argv.slice(2);
|
|
112
|
+
const jsonFlag = args.includes('--json');
|
|
113
|
+
const positional = args.filter(a => !a.startsWith('-'));
|
|
114
|
+
|
|
115
|
+
const command = positional[0];
|
|
116
|
+
|
|
117
|
+
if (command === 'resolve') {
|
|
118
|
+
const stageKey = positional[1];
|
|
119
|
+
|
|
120
|
+
if (!stageKey) {
|
|
121
|
+
const msg = 'Usage: gsd-t-model-tier-policy.cjs resolve <stageKey> [--json]';
|
|
122
|
+
if (jsonFlag) {
|
|
123
|
+
process.stdout.write(JSON.stringify({ ok: false, error: msg }) + '\n');
|
|
124
|
+
} else {
|
|
125
|
+
process.stderr.write(msg + '\n');
|
|
126
|
+
}
|
|
127
|
+
process.exit(1);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const tier = STAGE_TIERS[stageKey];
|
|
131
|
+
const modelId = resolve(stageKey);
|
|
132
|
+
|
|
133
|
+
if (modelId === null) {
|
|
134
|
+
const envelope = { ok: false, stageKey, error: `Unknown stage key: "${stageKey}"` };
|
|
135
|
+
if (jsonFlag) {
|
|
136
|
+
process.stdout.write(JSON.stringify(envelope) + '\n');
|
|
137
|
+
} else {
|
|
138
|
+
process.stderr.write(`Unknown stage key: "${stageKey}"\n`);
|
|
139
|
+
}
|
|
140
|
+
process.exit(1);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const envelope = {
|
|
144
|
+
ok: true,
|
|
145
|
+
stageKey,
|
|
146
|
+
tier,
|
|
147
|
+
model: modelId,
|
|
148
|
+
requiresThinkingOmitted: requiresThinkingOmitted(modelId),
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
if (jsonFlag) {
|
|
152
|
+
process.stdout.write(JSON.stringify(envelope) + '\n');
|
|
153
|
+
} else {
|
|
154
|
+
process.stdout.write(`stageKey: ${stageKey}\ntier: ${tier}\nmodel: ${modelId}\nrequiresThinkingOmitted: ${envelope.requiresThinkingOmitted}\n`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
process.exit(0);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Unknown command
|
|
161
|
+
const usage = `Usage: gsd-t-model-tier-policy.cjs resolve <stageKey> [--json]`;
|
|
162
|
+
if (jsonFlag) {
|
|
163
|
+
process.stdout.write(JSON.stringify({ ok: false, error: usage }) + '\n');
|
|
164
|
+
} else {
|
|
165
|
+
process.stderr.write(usage + '\n');
|
|
166
|
+
}
|
|
167
|
+
process.exit(1);
|
|
168
|
+
}
|
package/bin/gsd-t-parallel.cjs
CHANGED
|
@@ -36,6 +36,8 @@ const path = require("node:path");
|
|
|
36
36
|
const { buildTaskGraph, getReadyTasks } = require(path.join(__dirname, "gsd-t-task-graph.cjs"));
|
|
37
37
|
const { validateDepGraph } = require(path.join(__dirname, "gsd-t-depgraph-validate.cjs"));
|
|
38
38
|
const { proveDisjointness } = require(path.join(__dirname, "gsd-t-file-disjointness.cjs"));
|
|
39
|
+
// M85: single source of truth for model ids — sourced from policy module, never re-hardcoded here
|
|
40
|
+
const { MODEL_IDS } = require(path.join(__dirname, "gsd-t-model-tier-policy.cjs"));
|
|
39
41
|
// M61 D3: gsd-t-economics retired. estimateTaskFootprint produced a per-task
|
|
40
42
|
// token+cost estimate the planner could consult for in-session-headroom
|
|
41
43
|
// math. Native budget primitives (Workflow `budget` + /usage) replace it.
|
|
@@ -420,14 +422,18 @@ function _runCacheWarmProbe(opts) {
|
|
|
420
422
|
"then reply with the single word `warm` and nothing else:\n" +
|
|
421
423
|
filesRead.map((f) => `- ${f}`).join("\n");
|
|
422
424
|
|
|
423
|
-
|
|
424
|
-
|
|
425
|
+
// M85: pass model via --model flag ONLY (env var ANTHROPIC_MODEL is silently
|
|
426
|
+
// ignored by the current claude CLI — measured probe 2026-06-09 r3: env form
|
|
427
|
+
// ran opus-4-8 regardless of the env value). No env mutation here.
|
|
428
|
+
const env = process.env;
|
|
429
|
+
const cliArgs = ["-p", prompt, "--dangerously-skip-permissions"];
|
|
430
|
+
if (model) cliArgs.push("--model", model);
|
|
425
431
|
|
|
426
432
|
try {
|
|
427
433
|
// GSD-T-LINT: skip stream-json (reason: cache-warm probe — single-word "warm" reply, no progress to stream)
|
|
428
434
|
const r = spawnSync(
|
|
429
435
|
"claude",
|
|
430
|
-
|
|
436
|
+
cliArgs,
|
|
431
437
|
{
|
|
432
438
|
cwd: projectDir,
|
|
433
439
|
env,
|
|
@@ -580,11 +586,15 @@ function runDispatch(opts) {
|
|
|
580
586
|
// A task can opt back to Opus by declaring "[opus]" in its tasks.md line;
|
|
581
587
|
// the planner surfaces this via per-task metadata (future; today the per-
|
|
582
588
|
// subset opt-in is an all-or-nothing knob passed by the caller).
|
|
583
|
-
const DEFAULT_WORKER_MODEL =
|
|
589
|
+
const DEFAULT_WORKER_MODEL = MODEL_IDS.sonnet;
|
|
590
|
+
// M85: alias map sources from policy module — MODEL_IDS is the single authority.
|
|
591
|
+
// No bare model-id literals here; changing a model id in the policy module alone
|
|
592
|
+
// is sufficient (single-source thesis, AC b).
|
|
584
593
|
const modelAlias = {
|
|
585
|
-
opus:
|
|
586
|
-
|
|
587
|
-
|
|
594
|
+
opus: MODEL_IDS.opus,
|
|
595
|
+
fable: MODEL_IDS.fable,
|
|
596
|
+
sonnet: MODEL_IDS.sonnet,
|
|
597
|
+
haiku: MODEL_IDS.haiku,
|
|
588
598
|
};
|
|
589
599
|
const callerModel = opts && opts.workerModel;
|
|
590
600
|
const workerModel = callerModel === false
|
package/bin/gsd-t.js
CHANGED
|
@@ -1186,6 +1186,8 @@ const GLOBAL_BIN_TOOLS = [
|
|
|
1186
1186
|
"gsd-t-competition-judge.cjs",
|
|
1187
1187
|
// M83 — Plan-phase acceptance-traceability gate.
|
|
1188
1188
|
"gsd-t-traceability-gate.cjs",
|
|
1189
|
+
// M85 — Model-tier policy single source of truth (resolver + predicate).
|
|
1190
|
+
"gsd-t-model-tier-policy.cjs",
|
|
1189
1191
|
];
|
|
1190
1192
|
|
|
1191
1193
|
function installGlobalBinTools() {
|
|
@@ -2479,6 +2481,9 @@ const PROJECT_BIN_TOOLS = [
|
|
|
2479
2481
|
"gsd-t-competition-judge.cjs", "gsd-t-file-disjointness.cjs",
|
|
2480
2482
|
// M83 — Plan-phase acceptance-traceability gate (runs in the plan workflow).
|
|
2481
2483
|
"gsd-t-traceability-gate.cjs",
|
|
2484
|
+
// M85 — Model-tier policy resolver, so command invokers in consumer projects
|
|
2485
|
+
// can resolve stage tiers at invoke time (M69 injection pattern).
|
|
2486
|
+
"gsd-t-model-tier-policy.cjs",
|
|
2482
2487
|
];
|
|
2483
2488
|
|
|
2484
2489
|
// Files that older versions of this installer copied into project bin/ but
|
|
@@ -4575,6 +4580,16 @@ if (require.main === module) {
|
|
|
4575
4580
|
});
|
|
4576
4581
|
process.exit(res.status == null ? 1 : res.status);
|
|
4577
4582
|
}
|
|
4583
|
+
case "model-tier-policy": {
|
|
4584
|
+
// M85 — `gsd-t model-tier-policy` thin dispatcher to the tier-policy
|
|
4585
|
+
// resolver (single source of truth for model-tier assignments).
|
|
4586
|
+
const { spawnSync } = require("child_process");
|
|
4587
|
+
const js = path.join(__dirname, "gsd-t-model-tier-policy.cjs");
|
|
4588
|
+
const res = spawnSync(process.execPath, [js, ...args.slice(1)], {
|
|
4589
|
+
stdio: "inherit",
|
|
4590
|
+
});
|
|
4591
|
+
process.exit(res.status == null ? 1 : res.status);
|
|
4592
|
+
}
|
|
4578
4593
|
case "metrics":
|
|
4579
4594
|
doMetrics(args.slice(1));
|
|
4580
4595
|
break;
|
package/bin/model-selector.js
CHANGED
|
@@ -16,11 +16,14 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
// ── Tiers ───────────────────────────────────────────────────────────────────
|
|
19
|
+
// M85: FABLE tier added alongside HAIKU/SONNET/OPUS.
|
|
20
|
+
// Contract: .gsd-t/contracts/model-tier-policy-contract.md v1.0.0 § "Stage Policy"
|
|
19
21
|
|
|
20
22
|
const TIERS = Object.freeze({
|
|
21
23
|
HAIKU: "haiku",
|
|
22
24
|
SONNET: "sonnet",
|
|
23
25
|
OPUS: "opus",
|
|
26
|
+
FABLE: "fable",
|
|
24
27
|
});
|
|
25
28
|
|
|
26
29
|
const DEFAULT_TIER = TIERS.SONNET;
|
|
@@ -90,9 +93,16 @@ const PHASE_RULES = Object.freeze([
|
|
|
90
93
|
{ phase: "integrate", model: TIERS.SONNET, reason: "Integration wiring is routine coordination work" },
|
|
91
94
|
|
|
92
95
|
// Phase: debug
|
|
93
|
-
{ phase: "debug", task_type: "fix_apply",
|
|
94
|
-
{ phase: "debug", task_type: "root_cause",
|
|
95
|
-
|
|
96
|
+
{ phase: "debug", task_type: "fix_apply", model: TIERS.SONNET, reason: "Applying a known fix is routine code work" },
|
|
97
|
+
{ phase: "debug", task_type: "root_cause", model: TIERS.OPUS, reason: "Root-cause analysis is high-stakes reasoning" },
|
|
98
|
+
// M85: cycle-2 escalation — when debug cycle-1 (opus) has not resolved the issue,
|
|
99
|
+
// cycle-2 escalates to Fable. The debug DEFAULT (cycle-1/general) remains opus —
|
|
100
|
+
// no existing rule is altered (AC f, no silent degradation). This is a DOCUMENTED
|
|
101
|
+
// MIRROR for Task-based/bin/ callers; the live enforcement is in the debug workflow
|
|
102
|
+
// ternary (D3-T3); the D4 lint guards that ternary.
|
|
103
|
+
// API shape: selectModel({ phase: "debug", task_type: "cycle_2_escalation" }) → fable
|
|
104
|
+
{ phase: "debug", task_type: "cycle_2_escalation", model: TIERS.FABLE, reason: "Cycle-2 debug escalation — Fable after opus cycle-1 has not resolved; no existing rule altered (AC f)" },
|
|
105
|
+
{ phase: "debug", model: TIERS.OPUS, reason: "Debug default is high-stakes — prefer opus unless the task_type says otherwise" },
|
|
96
106
|
|
|
97
107
|
// Phase: partition — high-stakes architectural decomposition
|
|
98
108
|
{ phase: "partition", model: TIERS.OPUS, reason: "Domain partitioning is architectural reasoning — high stakes" },
|
package/commands/gsd-t-help.md
CHANGED
|
@@ -495,6 +495,13 @@ Use these when user asks for help on a specific command:
|
|
|
495
495
|
- **CLI**: `gsd-t traceability-gate [--milestone <Mxx>] [--project-dir <dir>] [--tasks <file>]`. Exit 0 all traceable · 4 ≥1 untraceable AC (blocks execute) · 64 no tasks files.
|
|
496
496
|
- **Contract**: `.gsd-t/contracts/plan-hardening-contract.md` v1.0.0 STABLE.
|
|
497
497
|
|
|
498
|
+
### model-tier-policy (M85)
|
|
499
|
+
- **Summary**: SINGLE source of truth for GSD-T model-tier assignments. Publishes the authoritative tier set (haiku/sonnet/opus/fable), the 7 designated stage→tier mappings, and the `requiresThinkingOmitted(model)` predicate (encoding Fable's HTTP-400 breaking change ONCE). M85 slots the Fable tier into the 5 highest-leverage stages (solution-space probe, partition probe, competition judge, pre-mortem, Red Team) where one call's judgment gates the most downstream spend; competition producers STAY opus (M82 blindness invariant); debug cycle-1→opus, cycle-2→fable. A M71-family lint (`test/m85-workflow-tier-policy-lint.test.js`) proves every workflow `model:` literal matches the policy — a drifted literal FAILS the lint (mandatory negative test).
|
|
500
|
+
- **Files**: `bin/gsd-t-model-tier-policy.cjs` (zero external deps — installer invariant).
|
|
501
|
+
- **Use when**: Any phase that needs to resolve a concrete model id from a stage key at invoke time (M69 pattern). Workflows NEVER `require` this module (sandbox ban) — they use hard-coded tier alias literals the lint proves match the policy.
|
|
502
|
+
- **CLI**: `gsd-t model-tier-policy resolve <stageKey> [--json]`. Emits `{ok, stageKey, tier, model, requiresThinkingOmitted}`. Exit 0 resolved · 1 unknown stage key.
|
|
503
|
+
- **Contract**: `.gsd-t/contracts/model-tier-policy-contract.md` v1.0.0 STABLE.
|
|
504
|
+
|
|
498
505
|
## Unknown Command
|
|
499
506
|
|
|
500
507
|
If user asks for help on unrecognized command:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tekyzinc/gsd-t",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.4.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,58 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# gsd-t-ctx-cue.sh — GSD-T low-context visual cue (M85)
|
|
3
|
+
#
|
|
4
|
+
# A Stop hook: fires mechanically at the end of EVERY turn. Computes remaining
|
|
5
|
+
# context window % from the current session's JSONL (the same source the status
|
|
6
|
+
# line uses) and, when it drops below the threshold, prints a STRONG red banner
|
|
7
|
+
# so the user knows to checkpoint (/gsd-t-pause) and /clear before compaction.
|
|
8
|
+
#
|
|
9
|
+
# Deterministic by design — does not rely on the model remembering to check
|
|
10
|
+
# (per feedback_deterministic_orchestration). Synchronous (NOT async) so its
|
|
11
|
+
# stdout reaches the terminal. Fails silently/open on any error — a status cue
|
|
12
|
+
# must never block or break a turn.
|
|
13
|
+
#
|
|
14
|
+
# Threshold: default 40 (% left). Override with GSD_T_CTX_CUE_THRESHOLD.
|
|
15
|
+
# Window: 1,000,000 (Opus 4.7/4.8 + Sonnet 4.x); 200,000 (Haiku).
|
|
16
|
+
|
|
17
|
+
set -o pipefail
|
|
18
|
+
THRESHOLD="${GSD_T_CTX_CUE_THRESHOLD:-40}"
|
|
19
|
+
|
|
20
|
+
# The hook receives the same JSON on stdin that other hooks do.
|
|
21
|
+
input=$(cat 2>/dev/null)
|
|
22
|
+
|
|
23
|
+
cwd=$(printf '%s' "$input" | jq -r '.workspace.current_dir // .cwd // ""' 2>/dev/null)
|
|
24
|
+
[ -z "$cwd" ] && cwd="$PWD"
|
|
25
|
+
model=$(printf '%s' "$input" | jq -r '.model.id // ""' 2>/dev/null)
|
|
26
|
+
|
|
27
|
+
# Only act inside GSD-T projects (a .gsd-t dir present) — the cue is GSD-T's.
|
|
28
|
+
[ -d "${cwd}/.gsd-t" ] || exit 0
|
|
29
|
+
|
|
30
|
+
proj_slug=$(printf '%s' "$cwd" | sed 's:/:-:g')
|
|
31
|
+
sess_dir="$HOME/.claude/projects/$proj_slug"
|
|
32
|
+
[ -d "$sess_dir" ] || exit 0
|
|
33
|
+
latest=$(ls -t "$sess_dir"/*.jsonl 2>/dev/null | head -1)
|
|
34
|
+
[ -n "$latest" ] || exit 0
|
|
35
|
+
|
|
36
|
+
case "$model" in
|
|
37
|
+
*haiku*) win=200000 ;;
|
|
38
|
+
*) win=1000000 ;;
|
|
39
|
+
esac
|
|
40
|
+
|
|
41
|
+
used=$(grep '"usage"' "$latest" 2>/dev/null | tail -1 \
|
|
42
|
+
| jq -r '(.message.usage // {}) | (.input_tokens//0)+(.cache_creation_input_tokens//0)+(.cache_read_input_tokens//0)' 2>/dev/null)
|
|
43
|
+
[ -n "$used" ] && [ "$used" -gt 0 ] 2>/dev/null || exit 0
|
|
44
|
+
|
|
45
|
+
pct=$(awk -v u="$used" -v w="$win" 'BEGIN { printf "%d", (100 - (u / w * 100)) + 0.5 }')
|
|
46
|
+
|
|
47
|
+
# Above threshold → silent (no cue).
|
|
48
|
+
[ "$pct" -lt "$THRESHOLD" ] 2>/dev/null || exit 0
|
|
49
|
+
|
|
50
|
+
# ── Strong red banner ──────────────────────────────────────────────────────
|
|
51
|
+
RED=$'\033[1;37;41m' # bold white on red
|
|
52
|
+
RST=$'\033[0m'
|
|
53
|
+
BAR="████████████████████████████████████████"
|
|
54
|
+
printf '\n%s %s %s\n' "$RED" "$BAR" "$RST"
|
|
55
|
+
printf '%s ⚠ CONTEXT LOW — %d%% LEFT %s\n' "$RED" "$pct" "$RST"
|
|
56
|
+
printf '%s Checkpoint now: /gsd-t-pause → /clear → /gsd-t-resume %s\n' "$RED" "$RST"
|
|
57
|
+
printf '%s %s %s\n\n' "$RED" "$BAR" "$RST"
|
|
58
|
+
exit 0
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Claude Code status line — GSD-T project status bar (CANONICAL SOURCE)
|
|
3
|
+
#
|
|
4
|
+
# This is the SHIPPED source of truth for the GSD-T status line. The installer
|
|
5
|
+
# copies it to ~/.claude/statusline-command.sh and wires the `statusLine` setting
|
|
6
|
+
# to it, so edits here survive `gsd-t install` / `update` / `update-all`.
|
|
7
|
+
# (Supersedes scripts/gsd-t-statusline.js, whose context source was retired in M61.)
|
|
8
|
+
#
|
|
9
|
+
# Layout (M85):
|
|
10
|
+
# Line 1: [GSD-T] | vX.Y.ZZ | ctx N% left | project | git branch | model id | HH:MM TZ
|
|
11
|
+
# Line 2: the full milestone/Status string (wraps to its own row instead of
|
|
12
|
+
# being truncated with a trailing "…" at terminal width).
|
|
13
|
+
set -o pipefail
|
|
14
|
+
|
|
15
|
+
input=$(cat)
|
|
16
|
+
|
|
17
|
+
# --- 1. [GSD-T] prefix (bright cyan when ANSI available) ---
|
|
18
|
+
PREFIX=$'\033[1;36m[GSD-T]\033[0m'
|
|
19
|
+
|
|
20
|
+
# --- 1b. GSD-T version — the installed framework version, project-independent.
|
|
21
|
+
# Source from ~/.claude/.gsd-t-version (written by the installer/update-all);
|
|
22
|
+
# fall back to the global package.json, then to empty (field omitted). ---
|
|
23
|
+
gsdt_version=""
|
|
24
|
+
if [ -f "$HOME/.claude/.gsd-t-version" ]; then
|
|
25
|
+
gsdt_version=$(tr -d '[:space:]' < "$HOME/.claude/.gsd-t-version" 2>/dev/null)
|
|
26
|
+
fi
|
|
27
|
+
if [ -z "$gsdt_version" ] && command -v gsd-t >/dev/null 2>&1; then
|
|
28
|
+
gsdt_version=$(gsd-t --version 2>/dev/null | tr -d '[:space:]')
|
|
29
|
+
fi
|
|
30
|
+
[ -n "$gsdt_version" ] && gsdt_version="v${gsdt_version#v}"
|
|
31
|
+
|
|
32
|
+
# --- 2. Project name — basename of cwd from JSON ---
|
|
33
|
+
cwd=$(printf '%s' "$input" | jq -r '.workspace.current_dir // .cwd // ""')
|
|
34
|
+
project=""
|
|
35
|
+
if [ -n "$cwd" ]; then
|
|
36
|
+
project=$(basename "$cwd")
|
|
37
|
+
fi
|
|
38
|
+
|
|
39
|
+
# --- 3. Milestone + phase — single grep of .gsd-t/progress.md ---
|
|
40
|
+
milestone=""
|
|
41
|
+
if [ -n "$cwd" ] && [ -f "${cwd}/.gsd-t/progress.md" ]; then
|
|
42
|
+
milestone=$(grep -m1 '^## Status:' "${cwd}/.gsd-t/progress.md" | sed 's/^## Status:[[:space:]]*//' | tr -d '\r')
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
# --- 4. Git branch (skip gracefully if not a repo) ---
|
|
46
|
+
branch=""
|
|
47
|
+
if [ -n "$cwd" ]; then
|
|
48
|
+
branch=$(git -C "$cwd" rev-parse --abbrev-ref HEAD 2>/dev/null || true)
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# --- 5. Model id ---
|
|
52
|
+
model=$(printf '%s' "$input" | jq -r '.model.id // ""')
|
|
53
|
+
|
|
54
|
+
# --- 6. Context window % left (M61: read latest usage envelope from Claude
|
|
55
|
+
# Code's session JSONL; falls back silently if unreadable).
|
|
56
|
+
# Window: 1,000,000 for Opus 4.7/4.8 + Sonnet 4.x; 200,000 for Haiku.
|
|
57
|
+
# Computed as input_tokens + cache_creation_input_tokens +
|
|
58
|
+
# cache_read_input_tokens to capture the full window pressure. ---
|
|
59
|
+
ctx_left=""
|
|
60
|
+
if [ -n "$cwd" ]; then
|
|
61
|
+
proj_slug=$(printf '%s' "$cwd" | sed 's:/:-:g')
|
|
62
|
+
sess_dir="$HOME/.claude/projects/$proj_slug"
|
|
63
|
+
if [ -d "$sess_dir" ]; then
|
|
64
|
+
latest_jsonl=$(ls -t "$sess_dir"/*.jsonl 2>/dev/null | head -1)
|
|
65
|
+
if [ -n "$latest_jsonl" ]; then
|
|
66
|
+
# Window size by model family. Haiku = 200k; everything else = 1M.
|
|
67
|
+
case "$model" in
|
|
68
|
+
*haiku*) win=200000 ;;
|
|
69
|
+
*) win=1000000 ;;
|
|
70
|
+
esac
|
|
71
|
+
# Grab the last "usage" record in the file and sum input fields.
|
|
72
|
+
used=$(grep '"usage"' "$latest_jsonl" 2>/dev/null \
|
|
73
|
+
| tail -1 \
|
|
74
|
+
| jq -r '
|
|
75
|
+
(.message.usage // {})
|
|
76
|
+
| (.input_tokens // 0)
|
|
77
|
+
+ (.cache_creation_input_tokens // 0)
|
|
78
|
+
+ (.cache_read_input_tokens // 0)
|
|
79
|
+
' 2>/dev/null)
|
|
80
|
+
if [ -n "$used" ] && [ "$used" -gt 0 ] 2>/dev/null; then
|
|
81
|
+
ctx_left=$(awk -v u="$used" -v w="$win" \
|
|
82
|
+
'BEGIN { p = 100 - (u / w * 100); printf "ctx %d%% left", (p + 0.5) }')
|
|
83
|
+
fi
|
|
84
|
+
fi
|
|
85
|
+
fi
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
# --- 7. Local time ---
|
|
89
|
+
timestamp=$(date +"%H:%M %Z")
|
|
90
|
+
|
|
91
|
+
# --- Assemble ---
|
|
92
|
+
# Line 1: short fields only. ctx% sits right after the version (per user).
|
|
93
|
+
# The verbose milestone status moves to line 2 so it wraps instead of
|
|
94
|
+
# being truncated with a trailing "…" at terminal width.
|
|
95
|
+
parts=("$PREFIX")
|
|
96
|
+
[ -n "$gsdt_version" ] && parts+=("$gsdt_version")
|
|
97
|
+
[ -n "$ctx_left" ] && parts+=("$ctx_left")
|
|
98
|
+
[ -n "$project" ] && parts+=("$project")
|
|
99
|
+
[ -n "$branch" ] && parts+=("$branch")
|
|
100
|
+
[ -n "$model" ] && parts+=("$model")
|
|
101
|
+
parts+=("$timestamp")
|
|
102
|
+
|
|
103
|
+
# Join line 1 with " | "
|
|
104
|
+
line1=""
|
|
105
|
+
for part in "${parts[@]}"; do
|
|
106
|
+
if [ -z "$line1" ]; then
|
|
107
|
+
line1="$part"
|
|
108
|
+
else
|
|
109
|
+
line1="${line1} | ${part}"
|
|
110
|
+
fi
|
|
111
|
+
done
|
|
112
|
+
|
|
113
|
+
# Line 2: the milestone/status string on its own line (Claude Code renders \n as a
|
|
114
|
+
# second status row). Omitted when there's no milestone status.
|
|
115
|
+
if [ -n "$milestone" ]; then
|
|
116
|
+
printf '%s\n%s' "$line1" "$milestone"
|
|
117
|
+
else
|
|
118
|
+
printf '%s' "$line1"
|
|
119
|
+
fi
|
|
@@ -295,7 +295,7 @@ After the E2E suite, `gsd-t-verify` Step 4.5 runs `gsd-t test-data --purge --run
|
|
|
295
295
|
Every code-producing phase ends with `gsd-t-verify.workflow.js`, which runs three orthogonal validators as `parallel()` `agent()` stages with schema-validated output. Per `.gsd-t/contracts/orthogonal-validation-contract.md` v1.0.0 STABLE, they are declared orthogonal objective functions — no collapse, no substitution, no transitive trust.
|
|
296
296
|
|
|
297
297
|
- **`/code-review ultra`** — cooperative correctness + cleanup. Severity: `important` / `nit` / `pre-existing`. Skippable via `args.skipUltra=true` + `args.skipUltraReason`. `skipUltra=true` is INELIGIBLE for `VERIFIED`.
|
|
298
|
-
- **Red Team** — adversarial / security / boundaries. Non-skippable. Protocol: `templates/prompts/red-team-subagent.md`. Verdict: `FAIL` (any CRITICAL or HIGH bug — blocks completion) or `GRUDGING-PASS` (exhaustive search, nothing found). CRITICAL/HIGH bugs get up to 2 fix cycles before deferral. Runs on `model: "
|
|
298
|
+
- **Red Team** — adversarial / security / boundaries. Non-skippable. Protocol: `templates/prompts/red-team-subagent.md`. Verdict: `FAIL` (any CRITICAL or HIGH bug — blocks completion) or `GRUDGING-PASS` (exhaustive search, nothing found). CRITICAL/HIGH bugs get up to 2 fix cycles before deferral. Runs on `model: "fable"` (M85).
|
|
299
299
|
- **QA** — test execution + shallow-test detection + contract compliance. Non-skippable. Protocol: `templates/prompts/qa-subagent.md`. Writes ZERO feature code. Any shallow E2E test blocks phase completion. Runs on `model: "sonnet"`.
|
|
300
300
|
|
|
301
301
|
When `.gsd-t/contracts/design-contract.md` or `.gsd-t/contracts/design/` exists, a fourth stage runs Design Verification (protocol: `templates/prompts/design-verify-subagent.md`) — opens a browser, compares the build against the design, returns a structured element-by-element MATCH/DEVIATION schema. Deviations block completion.
|
|
@@ -304,12 +304,13 @@ Synthesis stage merges results without category collapse. Verdict: `VERIFIED` /
|
|
|
304
304
|
|
|
305
305
|
## Model Display (MANDATORY)
|
|
306
306
|
|
|
307
|
-
**Each Workflow `agent()` call declares its model explicitly** via the `model:` option (`"haiku"` / `"sonnet"` / `"opus"`). The Workflow runtime emits a `⚙ [{model}] {label}` line per stage in `/workflows`, giving the user real-time visibility into which model handles each operation.
|
|
307
|
+
**Each Workflow `agent()` call declares its model explicitly** via the `model:` option (`"haiku"` / `"sonnet"` / `"opus"` / `"fable"`). The Workflow runtime emits a `⚙ [{model}] {label}` line per stage in `/workflows`, giving the user real-time visibility into which model handles each operation.
|
|
308
308
|
|
|
309
309
|
**Model assignments:**
|
|
310
310
|
- `model: "haiku"` — strictly mechanical tasks: run test suites and report counts, check file existence, validate JSON structure, branch guard checks
|
|
311
311
|
- `model: "sonnet"` — mid-tier reasoning: routine code changes, standard refactors, test writing, QA evaluation, straightforward synthesis
|
|
312
|
-
- `model: "opus"` — high-stakes reasoning: architecture decisions, security analysis, complex debugging, cross-module refactors,
|
|
312
|
+
- `model: "opus"` — high-stakes reasoning: architecture decisions, security analysis, complex debugging, cross-module refactors, quality judgment on critical paths
|
|
313
|
+
- `model: "fable"` — highest-stakes calls where one judgment gates the most downstream spend (M85): solution-space probe, partition probe, competition judge, pre-mortem, Red Team. Competition producers STAY `opus` (M82 blindness invariant — judge must differ from producers). Debug cycle-1 → `opus`, cycle-2 → `fable` (escalation). **Single source of truth for tier assignments:** `bin/gsd-t-model-tier-policy.cjs` + `.gsd-t/contracts/model-tier-policy-contract.md` v1.0.0 STABLE. The M71-family lint (`test/m85-workflow-tier-policy-lint.test.js`) proves every workflow `model:` literal matches the policy and a drifted literal FAILS the lint (mandatory negative test).
|
|
313
314
|
|
|
314
315
|
**Context budget:** Workflow scripts receive a `budget` global (`budget.total`, `budget.spent()`, `budget.remaining()`) tied to the user's per-turn token target. Use it for dynamic loops (`while (budget.total && budget.remaining() > 50_000) { ... }`) or to scale fleet size. Opus 4.7/4.8 ship 1M context windows; the legacy meter at `bin/token-budget.cjs` was retired in M61 — use native `/context` for live in-session usage.
|
|
315
316
|
|
|
@@ -94,7 +94,7 @@ for (let cycle = 1; cycle <= 2; cycle++) {
|
|
|
94
94
|
label: `debug-cycle-${cycle}`,
|
|
95
95
|
phase: `Cycle ${cycle}`,
|
|
96
96
|
schema: DEBUG_CYCLE_SCHEMA,
|
|
97
|
-
model: "opus",
|
|
97
|
+
model: cycle === 1 ? "opus" : "fable",
|
|
98
98
|
}).catch((e) => ({
|
|
99
99
|
resolved: false,
|
|
100
100
|
rootCause: `agent error: ${e && e.message}`,
|
|
@@ -169,7 +169,7 @@ async function runSolutionSpaceProbe(projectDir, phaseName, { milestone, briefPa
|
|
|
169
169
|
`BIAS TOWARD COMPETING: if you are uncertain, or can name even two plausibly-different approaches, choose compete=true. A wasted competition costs ~3× this one phase; a missed-better-approach costs far more downstream (more pre-mortem blocks, more bugs, more verify cycles). Err on the side of generating options.`,
|
|
170
170
|
`Return JSON per the schema: { "compete": true|false, "reason": "<one sentence>", "approaches": ["<a>","<b>",...] }.`,
|
|
171
171
|
].filter(Boolean).join("\n");
|
|
172
|
-
const opts = { label: "solution-space-probe", schema: _PROBE_SCHEMA, model: "
|
|
172
|
+
const opts = { label: "solution-space-probe", schema: _PROBE_SCHEMA, model: "fable" };
|
|
173
173
|
if (phaseNameOpt) opts.phase = phaseNameOpt;
|
|
174
174
|
const r = await agent(prompt, opts).catch(() => null);
|
|
175
175
|
// Probe failure → bias toward competing (fail-toward-options, per the cost logic).
|
|
@@ -195,7 +195,7 @@ async function runPartitionProbe(projectDir, { milestone, briefPath, userInput,
|
|
|
195
195
|
`BIAS TOWARD COMPETING: if ≥3 files/areas are in play or you're unsure, choose compete=true — the file-disjointness oracle will objectively pick the most-parallelizable valid carving among the candidates, so competing is low-risk and high-reward.`,
|
|
196
196
|
`Return JSON per the schema.`,
|
|
197
197
|
].filter(Boolean).join("\n");
|
|
198
|
-
const opts = { label: "partition-probe", schema: _PROBE_SCHEMA, model: "
|
|
198
|
+
const opts = { label: "partition-probe", schema: _PROBE_SCHEMA, model: "fable" };
|
|
199
199
|
if (phaseNameOpt) opts.phase = phaseNameOpt;
|
|
200
200
|
const r = await agent(prompt, opts).catch(() => null);
|
|
201
201
|
if (!r || typeof r.compete !== "boolean") {
|
|
@@ -473,7 +473,7 @@ if (!competitionOn) {
|
|
|
473
473
|
`IMPORTANT: use the CANDIDATE LABEL (A, B, C…) shown above as the "id" in your scores.`,
|
|
474
474
|
].join("\n"),
|
|
475
475
|
{
|
|
476
|
-
label: "judge:rubric", phase: "Judge", model: "
|
|
476
|
+
label: "judge:rubric", phase: "Judge", model: "fable",
|
|
477
477
|
schema: {
|
|
478
478
|
type: "object", required: ["scores"], additionalProperties: true,
|
|
479
479
|
properties: { scores: { type: "array", items: { type: "object", additionalProperties: true } } },
|
|
@@ -653,7 +653,7 @@ if (phaseName === "plan" && result && result.status !== "failed") {
|
|
|
653
653
|
`Every blocking finding MUST convert to a concrete requiredTest the plan must adopt. Advisory notes are forbidden.`,
|
|
654
654
|
`Verdict BLOCK if any concrete, falsifiable failure condition lacks a named required test; else CLEARED. Return JSON per the schema.`,
|
|
655
655
|
].join("\n"),
|
|
656
|
-
{ label: "pre-mortem", phase: "Plan Hardening", schema: PRE_MORTEM_SCHEMA, model: "
|
|
656
|
+
{ label: "pre-mortem", phase: "Plan Hardening", schema: PRE_MORTEM_SCHEMA, model: "fable" }
|
|
657
657
|
).catch((e) => ({ verdict: "BLOCK", findings: [{ severity: "HIGH", condition: `pre-mortem agent error: ${e && e.message}`, requiredTest: "re-run pre-mortem" }], notes: "agent-error" }));
|
|
658
658
|
|
|
659
659
|
result.preMortem = preMortem;
|
|
@@ -304,7 +304,7 @@ const stages = [
|
|
|
304
304
|
`Verdict is FAIL if you found any CRITICAL or HIGH severity bug; GRUDGING-PASS`,
|
|
305
305
|
`if you searched exhaustively and found nothing. Return JSON per the schema.`,
|
|
306
306
|
].join("\n"),
|
|
307
|
-
{ label: "red-team", phase: "Orthogonal Triad", schema: RED_TEAM_SCHEMA, model: "
|
|
307
|
+
{ label: "red-team", phase: "Orthogonal Triad", schema: RED_TEAM_SCHEMA, model: "fable" }
|
|
308
308
|
),
|
|
309
309
|
|
|
310
310
|
// Stage C — QA (test execution + shallow-test detection + contract compliance)
|