mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 22:14:25 +01:00
* fix(capy): the install trailer says behind=<n> and its fix, so a stale install shows in every relayed trailer (field list 2 item 13, #2378) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(capy): a lease records its holder's start identity, so a pid reused after a wake is reclaimed; a skipped session-start upgrade ends fail behind, never ok (GBRA-105, GBRA-106) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(lease): stale needs proof (unreadable or malformed identities keep the lease), ps identity is timezone-independent, and reclaim never deletes a concurrent renewal Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(autoplan): enforced round cap and spend budget, deferred close, public export without machine paths, opt-in durable checkpoints (field list 2 W5-W7) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(lease): one directory lock for publish, prune and reclaim; reclaim rereads the record judged stale, so a renewal is never deleted or hidden Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(lease): the directory lock names its holder; broken only when the holder is dead or its pid reused, unlock removes only its own lock, touch exits 3 under a live holder Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(lease): breakers serialize on .lock.break and reread the lock before removing it; gstack-skill-start reports a lease the held lock kept out Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(lease): name the double-crash limit of breaking a dead lock Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(capy): --for review-voice install profile, no-Drive bootstrap command, reviewer prompt trailer (field list 2 W11) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> (cherry picked from commit89bf556c11) * test: pin review-voice row, helpers row and the no-Drive fallback in existing install tests Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> (cherry picked from commit7f6c70459a) * test: the DRIVER template names the reviewer trailer relay (pregate lane) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> (cherry picked from commit2545d9a6d2) * test(context-budget): capy skill eager ceiling for the review-voice subagent paragraph (W11; ratchet protocol, capture value) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(eval-plan): prereg diagnostics dry run (W8) and .gstack/eval-models.json lint (W9), WIP Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> (cherry picked from commitb509096218) * feat(eval-plan): W8/W9 tests, docs/eval-models.md, troubleshooting anchors, /eval-plan steps Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> (cherry picked from commit9335e45de2) * test(eval-plan): eval-plan/SKILL.md names only defined codes and accepted flags Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> (cherry picked from commita7a974d268) * test(review-voice): the launch line points at the verdict's bun-only directory (#3233's launch contract) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(third-party-actions): the Aside drive-offer detector matches the product name, not the English idiom (paid run 38988927989 tpa-absent-linux false positive) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(autoplan): export refuses artifact paths outside the run before writing; a deferred close keeps earlier undecided findings; reopening CEO grants Eng its rerun (review of #3234) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(autoplan-budget): bind the reservation expectation to lib/autoplan-budget's default estimate (test-of-test ratchet) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(evals): the docs observer accepts the spawned-prefixed gstack-skill-start --help read Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(windows): curate capy-review-voice out of the Windows subset (POSIX launcher contract) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: changelog entry for field list 2 wave A Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * chore: stamp v1.91.115.0 Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
90 lines
4.3 KiB
Markdown
90 lines
4.3 KiB
Markdown
# Eval model rules: `.gstack/eval-models.json`
|
|
|
|
A repository that has rules about which models its evals may use (for example
|
|
"Fable in smoke tests only", "no gpt-5.4-mini", "the newest model of each
|
|
family") writes them as data in `.gstack/eval-models.json`. `gstack-eval-plan`
|
|
checks every arm against them: `arms`, `dry-run`, `pilot start`, and every
|
|
call row of `price-gate`. gstack never reads these rules from prose in
|
|
`CLAUDE.md` or any other document. Plan: field list 2, W9
|
|
(`docs/designs/field-list-2-2026-10-16/PLAN.md`).
|
|
|
|
## The file
|
|
|
|
```json
|
|
{
|
|
"schema_version": 1,
|
|
"allow": ["claude-*", "gpt-*"],
|
|
"deny": ["gpt-5.4-mini"],
|
|
"scopes": { "full": { "deny": ["claude-fable-*"] } },
|
|
"families": {
|
|
"claude-opus": ["claude-opus-5-5", "claude-opus-5", "claude-opus-4-8"],
|
|
"claude-fable": ["claude-fable-5-1", "claude-fable-5"],
|
|
"gpt-sol": ["gpt-6.1-sol", "gpt-6-sol"]
|
|
}
|
|
}
|
|
```
|
|
|
|
| Key | Required | Meaning |
|
|
|---|---|---|
|
|
| `schema_version` | yes | `1` |
|
|
| `allow` | yes | Model ids or globs an arm may use. An id that no entry matches fails, so a misspelled or new model is never admitted by default. Use `["*"]` to allow everything not denied. |
|
|
| `deny` | no | Ids or globs no arm may use. Checked first. |
|
|
| `scopes` | no | Per eval scope (`smoke` or `full`), an extra `allow` and/or `deny` list. "Fable in smoke only" is `scopes.full.deny: ["claude-fable-*"]`. |
|
|
| `families` | no | Family name to literal ids, **newest first**. With this key present, an arm must be the newest id of its family (`EVAL_MODEL_OLD_GENERATION` otherwise), and an arm in no family fails `EVAL_MODEL_GENERATION_UNRESOLVED` instead of passing unchecked. |
|
|
|
|
Globs use `*` (any run of characters) and `?` (one character); every other
|
|
character is literal. A rule matches the id as written, the id without a
|
|
provider prefix (`openai/gpt-5.4-mini` matches `gpt-5.4-mini`), and a Claude id
|
|
without its date suffix (`claude-opus-5-5-20261001` matches `claude-opus-5-5`).
|
|
An unknown key, an unknown scope, a glob inside `families`, or an id listed in
|
|
two families makes the whole file invalid (`EVAL_MODELS_INVALID`), and then no
|
|
arm passes.
|
|
|
|
The rules apply whatever gstack's own model catalog (`lib/model-catalog.ts`)
|
|
holds: an uncatalogued model is allowed or denied by the file alone. The
|
|
catalog only checks that `families` is current. If a family lists the
|
|
catalog's current frontier or smart model after another id, the file is stale
|
|
and invalid.
|
|
|
|
## Order of checks
|
|
|
|
For each arm at its scope, the first rule that refuses decides:
|
|
|
|
1. `deny` → `EVAL_MODEL_DENIED`
|
|
2. `scopes.<scope>.deny` → `EVAL_MODEL_DENIED`
|
|
3. no `allow` entry matches → `EVAL_MODEL_DENIED` (unknown ids fail closed)
|
|
4. `scopes.<scope>.allow` is set and does not match → `EVAL_MODEL_DENIED`
|
|
5. `families` is set: in no family → `EVAL_MODEL_GENERATION_UNRESOLVED`; not the family's first id → `EVAL_MODEL_OLD_GENERATION`
|
|
|
|
## Scope
|
|
|
|
`price-gate` checks each call row at that row's `scope` (`GSTACK_EVAL_CALLS v1`,
|
|
`docs/eval-calls.md`). `arms`, `dry-run` and `pilot start` take
|
|
`--scope smoke|full`, which defaults to `full`. So a smoke-only model fails
|
|
unless the command says it is a smoke run.
|
|
|
|
## Which copy is read
|
|
|
|
By default the reviewed copy is read: `git show origin/<base>:.gstack/eval-models.json`
|
|
(`--base`, default origin/HEAD's branch). A branch cannot loosen the rules it
|
|
is checked against. When the working-tree copy differs, an
|
|
`EVAL_MODELS_NOTE:` line says so. `--models-from head` lints with the working-tree copy
|
|
instead. Use it for the PR that adds or changes the file; the source label
|
|
says `head (unreviewed)`. When neither copy exists, nothing is checked and
|
|
the line says `EVAL_MODELS: none ...`. When the base cannot be resolved but a
|
|
working-tree copy exists, the command refuses (`EVAL_MODELS_INVALID`) rather
|
|
than skipping the rules.
|
|
|
|
## Output
|
|
|
|
```
|
|
EVAL_MODEL: claude-fable-5-1 scope=full denied (scopes.full.deny claude-fable-*) where=dry-run --arms
|
|
EVAL_MODEL: claude-opus-5-5 scope=full ok (allow claude-*; newest in families.claude-opus) where=dry-run --arms
|
|
EVAL_MODELS: fail refused=1/2 source=origin/main@0123456789ab:.gstack/eval-models.json
|
|
```
|
|
|
|
Each refusal also prints a result line on stderr ending in its code. `dry-run`
|
|
records the check as `checks.model_rules` in `dry-run.json` and fails
|
|
`DRY_RUN_RESULT`. `pilot start` reserves nothing. `price-gate` writes no
|
|
receipt.
|