docs(loadouts): Opus 5.5 lite, detection order, Capy SKILL_ROOT, setup --loadout; troubleshooting anchors; changelog fragment (GSTA-161)

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
garrytanandcapy-ai[bot] committed 2026-10-31 13:30:39 +00:00
1 parent a1c38753de
commit 4aea96333d
7 files changed
+78 -5

No files matched your search

+1
View File
@@ -206,6 +206,7 @@ the new defaults.
| Validate periodic cases plus the weekly gate census on a branch | `gh workflow run evals-periodic.yml --ref <branch> -f include_gate_census=true` | CI runners + API spend | Pushed branch, `gh` with workflow rights |
| Run the opt-in ML, gitleaks and Swift checks | `gh workflow run platform-qualification.yml --ref <branch>` | Free CI runners | Pushed branch, `gh` with workflow rights |
| Render one module loadout as an eval arm ([loadouts](docs/loadouts.md)) | `bin/gstack-modules render --host claude --modules full,-section:<skill>/<id> --out-dir <dir>` (`--loadout full` for the control) | Free, about a second per host | Bun 1.4.2 |
| See which loadout a model gets, or publish it as Capy would ([loadouts](docs/loadouts.md#how-gstack-picks-a-loadout)) | `bin/gstack-modules resolve --model <id>`; `bin/gstack-modules install --model <id> --full-root ~/.claude/skills --store <dir>` | Free, under a second | Bun 1.4.2 |
| Look at past local eval runs | `bun run eval:list` / `eval:compare` / `eval:summary` | Free | Local eval history |
| See a case's pass rate across recent weekly runs | `bun run eval:pass-rates --case <case-id>` | Free | `gh` with repo read access |
| Measure one red case alone against the ship bar (MEETS 9/10) | `bun run scripts/ship-measure.ts measure --case <case-id> --round baseline` ([bar](docs/TESTING_INTERNALS.md#ship-measure)) | API spend for 10 trials (12 for behavior); asks above $2/trial, stops at $25/case | Same as paid coverage |
+1 -1
View File
@@ -30,7 +30,7 @@ With `GSTACK_CAPY=1`, a workflow started in an unattended or spawned session che
For Capy running GPT-6 Astra, read `${CODEX_HOME:-$HOME/.codex}/skills/gstack/SKILL.md` for routing, then `${CODEX_HOME:-$HOME/.codex}/skills/gstack-<skill>/SKILL.md` for the selected workflow. These are the generated GPT-6 Astra instructions, not the Claude source files.
For Claude Code, read `~/.claude/skills/gstack/SKILL.md` for routing and `~/.claude/skills/<skill>/SKILL.md` for the selected workflow. When launching a Claude Code session, tell it to load gstack and name the intended workflow. For Capy running a Claude model, the Claude instruction files can be read directly with the Capy adaptations below; that does not turn Capy into the Claude Code harness.
For Claude Code, read `~/.claude/skills/gstack/SKILL.md` for routing and `~/.claude/skills/<skill>/SKILL.md` for the selected workflow. When launching a Claude Code session, tell it to load gstack and name the intended workflow. For Capy running a Claude model, the Claude instruction files can be read directly with the Capy adaptations below; that does not turn Capy into the Claude Code harness. Add `--model <id>` (your model id from the thread's model line) to every `scripts/install.sh` run: when it prints `SKILL_ROOT: <dir>`, read `<dir>/gstack/SKILL.md` and `<dir>/<skill>/SKILL.md` instead and source the printed `GSTACK_LAUNCH_FILE` (`docs/loadouts.md`; only Opus 5.5 gets a smaller tree).
If no specific skill was requested, use the router's task-to-skill mapping. Read only the selected workflow and its referenced files. For other models, use the closest applicable instruction profile without claiming a model-specific profile was generated or evaluated for them.
+12
View File
@@ -0,0 +1,12 @@
**Capy sessions on Claude Opus 5.5 can read a smaller set of gstack skills (GSTA-161).** Before, every model read the full shared preamble on every skill. Now Opus 5.5 gets the `lite` loadout: the same skills without eleven coaching sections of the preamble, about 10.7 KB less on land-and-deploy. Every other model, and any model gstack is not told about, still gets the full skills, and the checkout's committed SKILL.md files stay full.
### Added
- **`lite` loadout and a model map** in `modules/loadouts.json`. `lite` drops the voice, writing-style, completeness, confusion, evidence, context-health, question-tuning, search-before-building, repo-mode, brain-health and context-recovery pieces and keeps the upgrade check and brain sync. `models` maps only `claude-opus-5-5` (and its dated snapshots) to it; a point release or any other suffix gets full.
- **Explicit model detection** (`lib/model-detect.ts`): an explicit loadout, then `--model`, then `GSTACK_MODEL`, else full. An unmapped, invalid or unreadable model id serves full and never errors (`LOADOUT_INVALID_MODEL` names an id that is not a plain model id).
- **`gstack-capy-install --model <id>`** (passed through `scripts/install.sh`): prints `LOADOUT` and `SKILL_ROOT` lines and, for lite, writes a second launch file that adds `GSTACK_SKILL_ROOT`. The install verdict and its key do not change.
- **`gstack-modules install` and `resolve`**: `install` publishes the lite tree under `<state root>/loadouts/<loadout>-<digest>/` as a complete skill tree (rendered files plus every tracked runtime asset), keyed by the selection, model, full root, host and source tree, reused only after its files re-hash against the lock, and never replaced in place. A failure serves full with `LOADOUT_FALLBACK`. `render --bind-model <id> --full-root <dir>` renders the same start lines for eval arms.
- **`./setup --loadout <name>`** for Claude Code: saved as `claude_loadout` and rendered through setup's own Claude render into the untracked render dir; `--loadout full` returns to the committed files.
- **The start line carries the model.** A lite skill's start command names its model and the full install root, so `gstack-skill-start` prints `LOADOUT` and `LOADOUT_RULE` lines, plus `LOADOUT_MISMATCH` when `GSTACK_MODEL` names another model, telling that session to read the full SKILL.md instead.
### Tests
- `test/model-detect.test.ts`, `test/module-loadouts-install.test.ts`, `test/loadout-setup.test.ts`, and new cases in `test/module-loadouts.test.ts` and `test/gstack-capy-install.test.ts` cover the mapping and its near misses, hostile model ids, the start-line rules, two models on one machine, runtime assets from `SKILL_ROOT`, identity changes, concurrent publishers, and `./setup --loadout`.
+1 -1
View File
@@ -93,7 +93,7 @@ gstack/
│ ├── test/ # Integration tests
│ └── dist/ # Compiled binary
├── agents-digest/ # Committed 2KB instruction-tier rules digest (gstack-AGENTS.md) for rules-reading hosts
├── modules/ # loadouts.json (+ loadouts.schema.json): model rungs, pinned modules and named loadouts; read by scripts/modules.ts, rendered by bin/gstack-modules (docs/loadouts.md)
├── modules/ # loadouts.json (+ loadouts.schema.json): model rungs, the model map, pinned modules and named loadouts; read by scripts/modules.ts and lib/model-detect.ts, rendered by bin/gstack-modules (docs/loadouts.md)
├── extension/ # Chrome extension (side panel + activity feed + CSS inspector)
├── lib/ # Shared libraries (aside-render.ts = local-HTML rendering, Aside first, engine fallback; design-catalog.ts = the typed design anti-pattern catalog every design skill renders from; design-lint/ = the static and render tiers behind bin/gstack-design-lint, ported from hallmark's sloplint; design-detect-contract.ts = detector sentinel vocabulary; design-md.ts = open DESIGN.md reader/writer; dom-dump-script.ts + generated dom-dump.js = rendered-DOM dump for the detector; review-evidence.ts = review-start receipt binding, computed freshness and shared-code snapshot eligibility; frontend-scope.ts; claude-bin.ts, error-handling.ts, worktree.ts, egress-receipt.ts, context-bill.ts, redact-engine.ts, tracker-guard.ts, version-source.ts, code-intelligence/)
│ └── diagram-render/ # Vendored mermaid + excalidraw runtimes, built into one offline bundle the renderer loads
+46 -3
View File
@@ -1,8 +1,8 @@
# Module loadouts
gstack can render a smaller or larger set of its instructions for a given model. Each toggleable piece is a **module** with an id. A **loadout** is a named set of modules, and `modules/loadouts.json` maps each model **rung** to a loadout. Today every rung loads `full`, which is the same render gstack has always shipped. A loadout changes only when the shared skill eval (GSTA-2) measures a smaller one that matches `full` within its preregistered margin.
gstack can render a smaller or larger set of its instructions for a given model. Each toggleable piece is a **module** with an id. A **loadout** is a named set of modules, and `modules/loadouts.json` maps each model **rung** to a loadout. Every rung loads `full`, which is the same render gstack has always shipped. A loadout changes only when a preregistered eval measures a smaller one that matches `full` within its margin.
Install-time model detection, which picks a loadout for the model you run, ships with the first loadout that is not `full`. Until then, loadouts are an eval tool: `bin/gstack-modules render` produces any loadout as a byte-exact tree with a lock file.
One model has its own loadout: Claude Opus 5.5 gets `lite` ([below](#lite-on-claude-opus-55)). Every other model, and any model gstack is not told about, gets `full`. `bin/gstack-modules render` also produces any loadout as a byte-exact tree with a lock file for evals.
## Terms
@@ -12,6 +12,7 @@ Install-time model detection, which picks a loadout for the model you run, ships
| Pinned | A module no loadout can remove where `full` renders it: the start command, plan-mode rules, the question format, completion status, the unattended profile, /ship's review army and adversarial pass, the router and `/gstack-upgrade`. A skill's `preamble-tier` still applies, so a tier-1 skill has no question-format section in any loadout. |
| Rung | How much scaffolding a model gets: `frontier`, `strong`, `mid`, `light`, or `unknown`. Model ids are matched after lowercasing and stripping a `provider/` prefix (`anthropic/claude-haiku-5-5` is `mid`). Rungs are separate from the frontier/smart tiers in `lib/model-catalog.ts`, which choose which model runs a role. |
| Loadout | `{ base, exclude, include, preamble_tier_max, note }`. `full` (include `*`) is always present. An unknown model gets `default.unknown`, which is `full`. |
| Model map | `models` in `modules/loadouts.json`: an exact model id to a loadout, checked before rungs. The id also matches its dated snapshot (`-YYYYMMDD`) and `-latest`, never a point release or any other suffix (`claude-opus-5-5` matches `claude-opus-5-5-20261101`, not `claude-opus-5-50` or `claude-opus-5-5-1`). |
## Commands
@@ -35,6 +36,48 @@ bin/gstack-modules render --host claude \
3. Excluding a section removes its STOP pointer, its Section-index row and its file. The step becomes `> Not loaded in the <loadout> loadout: skip <trigger>. Do not offer it, and treat it as not run.` Skeleton text that belongs to the section, such as office-hours' Phase 3.5 offer, sits inside `{{IF_SECTION:<id>}}…{{/IF_SECTION}}` in the template and goes with it.
4. The autoplan runner (`gstack-autoplan`) reads review methodology from the source tree, not from a rendered loadout, so autoplan-runner arms always measure `full` methodology.
## Lite on Claude Opus 5.5
`lite` is `full` minus eleven coaching pieces of the shared preamble: voice, writing style, completeness, confusion, evidence, context health, question tuning, search before building, repo mode, brain health and context recovery. It keeps every pinned piece, the upgrade check (the PROACTIVE and skill-prefix rules) and brain sync (the privacy stop-gate). Only `claude-opus-5-5` maps to it, because only Opus 5.5 has been measured: office-hours showed no loss on Opus 5.5 (GSTA-161 preamble run), and the land-and-deploy confirmation (GSTA-161, PR 1b) compared full, lite and no gstack on the same tasks. Haiku 5.5 leaned slightly against a larger cut on the same office-hours run, so small models stay on `full`. Two skills point at a preamble section lite drops: office-hours and plan-ceo-review name ETHOS.md through the "Search Before Building" section, so under lite they find ETHOS.md in the gstack checkout instead.
### How gstack picks a loadout
gstack never guesses the model. It uses, in order:
1. An explicit loadout: `./setup --loadout <name>` on Claude Code, or `bin/gstack-modules install --loadout <name>`. It is served as asked and bound to no model.
2. An explicit model id: `--model <id>` on the Capy installer (`scripts/install.sh --model <id>`, which passes it to `bin/gstack-capy-install`), else `GSTACK_MODEL`.
3. Nothing: `full`.
A model id is lowercased, its `provider/` prefix and a trailing `[…]` are dropped, and it must then be a plain id (`^[a-z0-9][a-z0-9._-]{0,79}$`). An unmapped id serves `full`; an id that is not plain also serves `full` and prints `LOADOUT_INVALID_MODEL`. Nothing reads Claude Code settings or an inherited loadout variable, so a Sonnet subagent of an Opus session gets `full` unless its own install names Opus.
### Capy
The Capy skill passes the thread's model id: `bash scripts/install.sh --check --for <skill> --model <id>`. The installer prints two lines before the launch lines:
```
LOADOUT: lite model=claude-opus-5-5 source=model-flag digest=3f2a9c01d4e7 reused=yes
SKILL_ROOT: /home/user/.gstack/loadouts/lite-3f2a9c01d4e7
```
Read skills from `SKILL_ROOT` (`<root>/gstack/SKILL.md` routes, `<root>/<skill>/SKILL.md` runs) and source the printed `GSTACK_LAUNCH_FILE`, which adds `GSTACK_SKILL_ROOT`. For `full`, `SKILL_ROOT` is `~/.claude/skills` and the launch file is the install verdict's own.
The lite tree is published by `bin/gstack-modules install` under `<state root>/loadouts/<loadout>-<digest12>/`, beside the full install; the checkout and its committed SKILL.md files stay the full render. The digest covers the selection, the bound model, the full root, host, overlay, plan mode and the source tree (the git HEAD tree plus any uncommitted change), so an upgrade or a different model gets a new folder and an unchanged one is reused after its files are re-hashed against `loadout.lock.json`. Each folder is a complete skill tree: the rendered SKILL.md and section files plus every tracked runtime asset of each skill (templates, references, scripts). A published folder is never replaced; the newest three per loadout are kept. Two Capy threads with different models on one machine share one install verdict and get one launch file each (`<verdict>.loadout-<name>-<digest>.env` for lite). Any failure serves `full` and prints `LOADOUT_FALLBACK`.
### The start line carries the model
A lite tree chosen for a model bakes that model and the full root into each skill's start command, so `gstack-skill-start` prints:
```
LOADOUT: lite model=claude-opus-5-5 full_root=/home/user/.claude/skills
LOADOUT_RULE: this file was rendered for claude-opus-5-5. If you are not running claude-opus-5-5, read /home/user/.claude/skills/qa/SKILL.md now and follow it instead of this file.
```
When `GSTACK_MODEL` is set to a different model, it adds `LOADOUT_MISMATCH`. A preamble already read cannot be unread, so the remedy is always to read the full file. An explicit loadout (`./setup --loadout lite`) carries no model and prints neither line.
### Claude Code
`./setup --loadout lite` saves `claude_loadout` and renders the Claude skills into the untracked render dir (`<state root>/render/claude`) through setup's own render, the same path a pinned overlay or `plan_mode_preamble=off` takes, so later upgrades and `gstack-config gbrain-refresh` keep it. `./setup --loadout full` returns to the committed files. A plain `./setup` never selects lite.
## Changing a loadout
Add or edit an entry under `loadouts` and cite the GSTA-2 run (preregistration, margin, per-rung result) in its `note` and in the PR body. A model-specific loadout that wins a preregistered comparison is allowed even if it breaks the expected nesting (light ⊇ mid ⊇ strong). Committed SKILL.md files are always the `full` render, so the parity suite, the SKILL.md size guard, the size ledger and the catalog and context budgets keep measuring `full`.
Add or edit an entry under `loadouts` (and `models`, for a model-specific one) and cite the measurement (preregistration, margin, per-rung result) in its `note` and in the PR body. A model-specific loadout that wins a preregistered comparison is allowed even if it breaks the expected nesting (light ⊇ mid ⊇ strong). Committed SKILL.md files are always the `full` render, so the parity suite, the SKILL.md size guard, the size ledger and the catalog and context budgets keep measuring `full`.
+15
View File
@@ -5535,6 +5535,21 @@ These codes come from `bin/gstack-modules` and `gen-skill-docs --loadout/--modul
**Meaning.** `--loadout` or the first token of `--modules` names no loadout in `modules/loadouts.json`. **Fix.** Use a name from the `valid:` list (`bin/gstack-modules list` prints them); `full` is always present.
<a id="loadout-invalid-model"></a>
### `LOADOUT_INVALID_MODEL: "<id>" is not a plain model id; using full`
**Meaning.** The model id given to the installer (`--model`) or in `GSTACK_MODEL` is not a plain id after normalizing (lowercase letters, digits, `.`, `_` and `-`, at most 80 characters), so it never reaches a launch file or a start line. The full skills are served; nothing failed. **Fix.** Pass the model id as your model list names it, for example `--model claude-opus-5-5` or `--model anthropic/claude-opus-5-5` ([loadouts](loadouts.md#how-gstack-picks-a-loadout)).
<a id="loadout-mismatch"></a>
### `LOADOUT_MISMATCH: <baked id> vs <running id>: read <full root>/<skill>/SKILL.md now`
**Meaning.** The skill you opened is the lite render made for one model, and `GSTACK_MODEL` says this session runs another. Lite is measured only on the baked model. **Fix.** Read the named full SKILL.md and follow it instead of the lite one. To stop it recurring, run the installer with this session's model (`scripts/install.sh --model <id>`) and read skills from the `SKILL_ROOT` it prints.
<a id="loadout-fallback"></a>
### `LOADOUT_FALLBACK: <reason>; serving full`
**Meaning.** A lite tree could not be published (a render or dangling-reference failure, an unwritable state root, or a published folder whose files no longer match its `loadout.lock.json`), so the installer served the full skills and printed `SKILL_ROOT` for them. Nothing else changed. **Fix.** Fix the named reason; for a folder whose files changed, delete `<state root>/loadouts/<loadout>-<digest>` and rerun the installer, which publishes it again.
<a id="module-spec-invalid"></a>
### `MODULE_SPEC_INVALID: …`
+2
View File
@@ -4,6 +4,8 @@
{ "file": "scripts/modules.ts", "movedFrom": "(new: module loadouts catalog and selection, GSTA-140)" },
{ "file": "scripts/modules-render.ts", "movedFrom": "(new: module loadout render and lock, GSTA-140)" },
{ "file": "scripts/modules-cli.ts", "movedFrom": "(new: bin/gstack-modules CLI, GSTA-140)" },
{ "file": "scripts/modules-install.ts", "movedFrom": "(new: lite loadout install tree with identity-keyed reuse, GSTA-161)" },
{ "file": "lib/model-detect.ts", "movedFrom": "(new: explicit model to loadout detection, GSTA-161)" },
{ "file": "lib/harness-register.ts", "movedFrom": "(new: gstack-harness registrar, GSTA-135)" },
{ "file": "lib/harness-qm.ts", "movedFrom": "(new: gstack-harness QM skill-pack target, GSTA-135)" },
{ "file": "lib/design-lint/render.ts", "movedFrom": "(new: the bundled design lint, GSTA-105; ported from hallmark sloplint.mjs)" },