diff --git a/.gstack/pregate.json b/.gstack/pregate.json index 1dded7b5f..8b61aafb9 100644 --- a/.gstack/pregate.json +++ b/.gstack/pregate.json @@ -31,5 +31,14 @@ "bun run test": { "profiles": ["ci-reuse/free-tests"], "inventory": "bun --no-env-file --config=/dev/null run scripts/test-free-shards.ts --list | sed -n 's/^ //p'" } }, "warn": [], + "guards": [ + { "id": "typecheck", "when": ["**/*.ts", "tsconfig.json", "package.json", "bun.lock"], "command": "bun run typecheck", "fix": "fix the type errors bun run typecheck prints" }, + { "id": "skill-size", "when": ["SKILL.md", "*/SKILL.md", "*/sections/*.md", "SKILL.md.tmpl", "*/SKILL.md.tmpl", "*/sections/*.tmpl", "*/sections/manifest.json", "*/templates/*.tmpl", "hosts/**", "model-overlays/**", "scripts/gen-skill-docs.ts", "scripts/resolvers/**", "test/skill-md-size-guard.test.ts", "test/skill-size-budget.test.ts", "test/catalog-budget.test.ts", "test/fixtures/skill-md-size-allowlist.json", "test/fixtures/parity-baseline-*.json", "test/helpers/skill-census.ts", "test/helpers/skill-startup.ts"], "command": "bun test test/skill-md-size-guard.test.ts test/skill-size-budget.test.ts test/catalog-budget.test.ts", "fix": "shorten the skill text the failure names; move a pin only when the growth is wanted, with the reason in the PR body" }, + { "id": "parity-suite", "when": ["SKILL.md", "*/SKILL.md", "*/sections/*.md", "SKILL.md.tmpl", "*/SKILL.md.tmpl", "*/sections/*.tmpl", "*/sections/manifest.json", "*/templates/*.tmpl", "hosts/**", "model-overlays/**", "scripts/gen-skill-docs.ts", "scripts/resolvers/**", "test/parity-suite.test.ts", "test/fixtures/parity-baseline-*.json", "test/helpers/parity-harness.ts"], "command": "bun test test/parity-suite.test.ts", "fix": "restore the missing phrase or heading, or shorten the skill back under its size ratio" }, + { "id": "context-budget", "when": ["SKILL.md", "*/SKILL.md", "*/sections/*.md", "SKILL.md.tmpl", "*/SKILL.md.tmpl", "*/sections/*.tmpl", "*/sections/manifest.json", "*/templates/*.tmpl", "hosts/**", "model-overlays/**", "scripts/gen-skill-docs.ts", "scripts/resolvers/**", "test/context-budget-ratchet.test.ts", "test/fixtures/context-budget*.json"], "command": "bun test test/context-budget-ratchet.test.ts", "fix": "shorten the skill under its token ceiling, or lower the ceiling when the skill shrank" }, + { "id": "module-size", "when": ["lib/**/*.ts", "bin/**", "scripts/**/*.ts", "browse/src/**/*.ts", "test/helpers/**/*.ts", "test/module-size-ratchet.test.ts", "test/fixtures/module-size-ratchet.json"], "command": "bun test test/module-size-ratchet.test.ts", "fix": "split the module or function the failure names; it may not grow past its recorded size" }, + { "id": "goldens", "when": ["SKILL.md", "*/SKILL.md", "*/sections/*.md", "SKILL.md.tmpl", "*/SKILL.md.tmpl", "*/sections/*.tmpl", "*/sections/manifest.json", "*/templates/*.tmpl", "hosts/**", "model-overlays/**", "scripts/gen-skill-docs.ts", "scripts/resolvers/**", "ship/**", "scripts/gen-ship-goldens.ts", "test/host-config.test.ts", "test/fixtures/golden/**"], "command": "bun test test/host-config.test.ts", "fix": "when the render change is intended, run bun scripts/gen-ship-goldens.ts and commit the updated goldens" }, + { "id": "doc-caps", "when": ["CLAUDE.md", "test/claude-md-size.test.ts"], "command": "bun test test/claude-md-size.test.ts", "fix": "move contributor-only detail from CLAUDE.md into docs/ and leave a one-line pointer" } + ], "strays": { "allow": ["modules/loadouts.json", "modules/loadouts.schema.json"] } } diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f411c10c..b6d3f5c18 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,81 @@ # Changelog +## [1.94.30.0] - 2026-11-03 + +**/ship sends more diffs to full review: spend-binding code, eval scorers, a repo's own risk paths and docs that print secrets now count as risk, and a lane can declare a class the matchers miss (GSTA-258 build C).** Today's reviewer panel stays the default. The one-reader shape for non-risk code ships as an opt-in (`review.panel risk`) because its no-loss replay didn't pass: with only the diff and no repository, the reader found 2 of 68 non-risk blockers, so the replay is being redone with readers working in a checkout. A repo with platform review on can skip /ship's readers on plain docs. + +### Added +- **A `review` key in `.gstack/ship-policy.json`**, read from the base branch only (needs `min_gstack_version` at the release that adds it): `panel` (`all` by default, today's panel; `risk` or `off` opt in to one reader), `reader_model` (`lane` by default), `platform` (`off` by default) and `risk_paths` (globs of code paths that count as risk class `policy`). +- **`gstack-review-plan` prints `REVIEW_MODE:`** and, under `review.platform on`, `PLATFORM_REVIEW:`. `DISPATCH:` lines name the reader's `model=` and `classes=`. New flags: `--fix-checks ` and `--pr `. +- **The platform depth.** Under `review.platform on`, a docs or tests diff with no numeric, results or prereg claims, whose PR carries capy-ai[bot]'s review at the exact head, plans `NEXT: platform` with a planned `SKIP:` for every reader. Step 11.5 accepts it, and `gstack-ship-receipt write --platform-review ` records `review_depth: platform` with that review's id and head. It never applies to runtime code, and never where platform review is off. +- **New risk classes**: `eval` (scorer, judge and grader paths) and `policy` (`review.risk_paths`). A docs line that can print a credential (an `Authorization:` header, a bearer value, a key-shaped literal, `mcp get`) is security risk (gbrain#8417). +- **The receipt records `review_depth.mode`** (lean, panel or platform). +- **Declared risk classes** (GST-1129). `gstack-review-plan --declare-risk ` and `gstack-ship-receipt write --declare-risk ` add classes the matchers missed. A declaration only raises the depth, the plan prints `RISK_DECLARED:`, and the receipt keeps `review_depth.declared` beside `review_depth.detected`. +- **Code that reserves, binds, leases or settles spend is money** (GST-1129): `bindCheck(ledger, …)`, `reserveSpend` or `bindCap` in a code file now plans full depth. gstack-evals#170 got its full review only because an unrelated `UBICLOUD_API_KEY` line was in the diff. + +### Changed +- **Opt-in one-reader review** (`review.panel risk`): non-risk code gets one native adversarial reader at the lane's reasoning, carrying the code checklists and the Red Team checklist, and risk class keeps the full panel. One fix-check follows a fixed blocker. It isn't the default until its no-loss replay passes. +- **Fewer false risk triggers** (harness-evals#231): committed `*.sqlite` and other binary data files count as data, never code; JSON, YAML and other config files are judged by their path, so a `lease_usd` value no longer means money; analysis scripts under `docs/`, `results/` or `benchmarks/` are judged by their path. +- The outside voice reads its diff from a file the brief names with its sha256, instead of a diff pasted into the prompt. Past 2,000,000 bytes it reads a path-scoped slice whose brief lists the files it left out, instead of stopping the pass. + +## [1.94.29.0] - 2026-11-03 + +**Every part of gstack now has to earn its place.** Garry, 2026-11-02: "Also work this principle of only include it if it is beneficial and good into gstack". A new skill, step, instruction, check or default needs evidence that it helps on frontier models, or a reason, an owner and a dated plan to measure it. The first inventory reads untested almost everywhere: one part, `/investigate`, has a fair no-effect result (a CUT-CANDIDATE, measured on Opus 5.5 only), 3 have a fair test under way, 14 more have one proposed, and 5 safety guards stay on their stated reason. + +### Added +- **ETHOS.md principle 9, Earn Its Place.** A fair test is Claude Code's own `claude_code` system prompt, frontier models (today Opus 5.5 and GPT-6.1 Sol) and a masked no-gstack arm. A part a fair test shows doesn't help gets one "Why null?" cycle, then is kept, fixed or cut. Measure the biggest parts first. The Capy factory page cites it in item 8, still under its 3,300-byte budget. +- **`docs/inventory/evidence.json`, the evidence ledger, and `bin/gstack-inventory`.** One row per part: every skill folder plus seven shared parts (preamble, catalog, the two Capy files, the /ship review panel and docs audit, the /autoplan phases), 71 keys in all. Each status has its own required fields (`helps`, `no-effect` and `hurts` need sources, frontier models, the preset prompt, a scope and a masked no-gstack arm, and the last two an R48 verdict; a new `untested` part needs a reason, a concrete plan, an owner and a `measure_by` date; `safety` needs a reason). Rows present today are grandfathered, seeded from the gstack-evals evidence table with their sources, plans and earlier results that don't count yet; no part added later can claim that (`test/fixtures/inventory-grandfathered.json`). `gstack-inventory` writes `docs/inventory.md` (parts ranked by load per run, a "measure next" order, preamble tiers, paid dispatches per run for /ship and /autoplan quoted from their own docs); `--check` validates the ledger; `--json` prints it. It works on the git repo you run it in and never writes into a gstack install. +- **`gstack-skill-bill` prints `EVIDENCE:`**, the skill's ledger row, and `/write-skill`'s edit ladder step 2 is now the evidence rule, with an `Evidence:` commit line beside `Skill-Size:`. +- **`/autoplan`'s CEO phase lists each part a plan adds to gstack** with its evidence or owned, dated plan; a missing one is a finding. + +### Changed +- **No-gstack arms must be masked.** `gstack-eval-plan prereg --check` now also requires `masking:`, `install:` and `audit:` in `## No-gstack arm` and refuses a blank or `none` value (`PREREG_NO_GSTACK_ARM`). On Capy, run agents inherit the instruction to load the user-drive gstack skill, so the arm hides the Capy drives and checks that no gstack is installed. An arm that read a gstack skill file is contaminated and gets zero credit (docs/TESTING_INTERNALS.md#no-gstack-arm). + +### For contributors +- CONTRIBUTING.md "Adding a new skill" adds the ledger row; `test/inventory.test.ts` fails on a skill without a valid row. + +## [1.94.28.0] - 2026-11-03 + +**`gstack-ship-preflight --timeout` reaches Step 5's pregate (GST-1196).** Preflight ran `gstack-pregate` with no way to pass `--timeout`, so on a repo whose `.gstack/pregate.json` sets no `timeout_s`, a test group longer than 600 s always stopped with `PREGATE_INCOMPLETE`. The only workaround was a separate `gstack-pregate --timeout 1800` before finish (gstack-evals #176 and #179). + +### Fixed +- **`gstack-ship-preflight`:** `--timeout ` (whole seconds, 1 to 3600) is passed to Step 5's pregate as its `--timeout` and overrides the policy's `timeout_s`. When it isn't set, `timeout_s` applies, and the default is 600 s, as the help now says. A bad value exits with usage (2). +- **`docs/pregate.md`:** the `timeout_s` row says preflight now forwards `--timeout`. + +### Tests +- `test/ship-preflight.test.ts`: `--timeout 2` bounds a 4 s test under a 600 s policy and `PREGATE_DETAIL` names `--timeout`; 0, 3601, 1.5 and x are usage errors. + +## [1.94.27.0] - 2026-11-03 + +- Housekeeping checks now run before every push instead of after a CI round (GST-1138, ship-90-90 plan). `gstack-pregate --stage guards --allow-repo-commands` runs only generated-file freshness (`regen`) and the repo's `guards`, each only for the files the branch touched, and writes `.gstack/tmp/pregate-guards.json` so it never replaces the full run's record. `gstack-ship-release push` runs it before every push, early or final, prints `GUARDS:`, and stops with the guard's fix when one fails, pushing nothing. A pass already recorded at HEAD's exact tree, by push itself or by `finish`'s preflight, is reused instead of rerun. See docs/pregate.md#prepush. +- gstack declares its own guards in `.gstack/pregate.json`: typecheck, the skill size guards, the parity suite, the context-budget ratchet, the module-size ratchet, the /ship goldens and the CLAUDE.md size cap. gbrain had 64 of 93 red heads from checks like these, and #3908 went red on the context-budget ratchet. Every gstack at or above this repo's version floor already reads the `guards` key, so the floor does not move. + +### Tests +- `test/pregate-guards.test.ts`: `--stage guards` runs only `regen` and `guards`, writes its own record and refuses `--only` and `--from-run`; gstack's guard list validates, names test files that exist and fires only on its own inputs. +- `test/ship-release.test.ts`: a failing guard stops the push with its fix and pushes nothing; a pass at the same tree is reused, and a new commit runs the guards again. + +On main, all five new tests fail. + +## [1.94.26.0] - 2026-11-03 + +- A lane whose command runs several `bun test --shard=i/N` processes now records the sum of the shards' counts, not the last shard's (GBEV-3, gbrain-evals#317: 3336 recorded against 6,095 run across 2 shards). The sum needs shards 1/N to N/N, in order, at the end of the log; any other set records the last summary as before, so a command that runs a suite twice, or reruns one shard, still reports its last run. The shard lines and count lines that scroll out of the 64 KiB output tail are kept for this, so a long shard run still sums (a real 2-shard, 2000-test bun run recorded 1000 before and 2000 after). +- docs/pregate.md#dispatch-carry warns that a `workflow_dispatch` run reports under the same job names on the head commit, so without strict branch protection a green dispatch run can hide a red merge-commit check. It recommends strict protection or distinct job names for dispatch runs, and notes that gstack's own merge trains test the merged tree. +- When a pull_request run tested a merge whose base moved after the run, `CI_CARRY: none (… tree_mismatch …)` now says so and prints the one-command fix: `gh workflow run --ref , then gstack-ci-carry wait` (TRUT-7). +- docs/pregate.md#skipped-files names the order of record-ci's checks after a skipped-file remainder, including the final `tree_changed` recheck. + +### Tests +- `test/ship-ci-carry.test.ts`: a pull_request run whose merge commit has another tree prints the base-moved reason and `gh workflow run free.yml --ref feat, then gstack-ci-carry wait`. It fails without the change. +- `test/evidence.test.ts`: two `--shard=i/2` processes in one log record 6095, two shards with more than 64 KiB of per-test lines record 4000, and an incomplete, repeated or rerun shard set records the last summary. The sum cases fail without the change (3336 and 2000). + +## [1.94.25.0] - 2026-11-03 + +- A lane label with a space or a colon, such as `pnpm lint` or `db:test`, no longer goes MISSING right after it carries (TRUT-7, truther#1170). `gstack-evidence` writes a label to the ledger with unsafe characters turned into `_`, but `check`, which `gstack-ship-release finish` and `accept` call, looked it up under the raw label, so /ship stopped with "not FRESH at the pushed head". `run`, `record-ci` and `check` now share one label spelling. +- A carry that doesn't reach FRESH no longer leaves a `ci_carried` entry in the ship receipt (TRUT-7: truther's receipt kept `pnpm_lint:41603968341/1` from the attempt that stopped). `gstack-evidence carried` now lists a CI row only when `check`'s own grading, at the 24-hour age /ship uses, calls it FRESH. + +### Tests +- `test/ship-ci-carry.test.ts`: `accept` with the lanes `pnpm lint` and `db:test` carries both and checks them FRESH, and `gstack-evidence check` finds both. It failed before the fix (`EVIDENCE: MISSING label=pnpm lint`). +- `test/ship-ci-carry.test.ts`: a carried `pnpm lint` row aged past 24 hours grades STALE and leaves `carried` empty. It failed before the fix (the stale row was still listed). + ## [1.94.24.0] - 2026-11-03 ### Fixed diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3df1c01ab..db050d961 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -814,6 +814,7 @@ Write or change skill prose with `/write-skill` (edit ladder, levels, `gstack-sk 3. The dynamic template discovery picks it up for every host render and for `gstack/llms.txt`. Two things it does not fill in: a row in `SKILL_REQUIREMENTS` in `lib/doctor-components.ts` (the doctor's `--for ` table; `test/doctor-components.test.ts` fails on a skill directory without one, and the same gen run rerenders `bin/gstack-doctor-components.sh`), and the catalog rows readers find the skill through (README.md, AGENTS.md, `docs/skills.md`, `docs/PROJECT_STRUCTURE.md`, the root `SKILL.md.tmpl` router) 4. Budget it: run `bun test/helpers/capture-context-budget.ts` and commit the refreshed `test/fixtures/context-budget.json` — the context-budget ratchet fails any skill without a ceiling; the catalog budget (`test/catalog-budget.test.ts`) fails until its cap is ratcheted by the protocol in its failure message, and the number is quoted in CLAUDE.md, this file and `docs/CONTRIBUTOR_REFERENCE.md` 5. Commit `{skill}/SKILL.md` (and the generated `{skill}/sections/preamble.md` when the template's `preamble-tier` is 2 or higher), external host output is generated at setup time and gitignored +6. Give it a row in the evidence ledger, `docs/inventory/evidence.json` ([ETHOS.md: 9. Earn Its Place](ETHOS.md#9-earn-its-place)): evidence that it helps on frontier models (`sources`, `models`, `prompt: preset`, `scope`, `masked: true`), or `status: untested` with its `reason`, a concrete `plan` (a `test/fixtures/skill-evals//tasks.json` or an /eval-plan preregistration), an `owner` and a `measure_by` date. `test/inventory.test.ts` fails on a skill without a valid row; `bin/gstack-inventory --check` names the problem, and `bin/gstack-inventory` refreshes the dated snapshot in `docs/inventory.md` ## Conductor workspaces diff --git a/ETHOS.md b/ETHOS.md index 0c631da19..aaac79291 100644 --- a/ETHOS.md +++ b/ETHOS.md @@ -273,6 +273,42 @@ the eval that jumps twenty points usually found a shortcut, not a capability. --- +## 9. Earn Its Place + +Garry, 2026-11-02: +"Also work this principle of only include it if it is beneficial and good into gstack". + +Every skill, step, instruction, check and default costs tokens and attention +on every run that loads it. So each one earns its place. A new part comes with +evidence that it helps on frontier models, or with a stated reason, an owner +and a date by which a fair test will measure it. + +A fair test runs Claude Code's own system prompt (the `claude_code` preset, +not the Agent SDK's stub), on frontier models (today Opus 5.5 and GPT-6.1 Sol, +plus at most one named low-cost model), against a no-gstack arm that is masked +so it cannot read gstack. A result from the stub, from Haiku or Sonnet alone, +or from an unmasked arm is a lead, not a verdict. + +When a fair test shows an existing part doesn't help, it gets one "Why null?" +cycle: did the model use the part, did the task fit, was there enough power and +room below the ceiling, and does today's model do the job without it. Then +keep it, fix the method, or cut it, and say which plainly. + +Safety and security guards (careful, freeze, guard, unfreeze, cso) stay on +their stated reason: they stop the rare bad run that an average score can't see. + +Measure the biggest parts first, the ones that load the most on the most runs. +[docs/inventory.md](docs/inventory.md) ranks every part by load and shows its +evidence; `docs/inventory/evidence.json` is the ledger, and +`bin/gstack-inventory --check` validates it. + +**Anti-patterns:** +- A new step because it sounds wise, with no test planned. +- Keeping a part because it looked good on the stub or a small model. +- Cutting a part after one null result, without the Why-null cycle. + +--- + ## How They Work Together Boil the Ocean says: **cover the whole requirement with the least code.** @@ -282,6 +318,7 @@ Attack the Premise says: **when fixes keep failing, test the belief they share.* Separate Before Serializing says: **remove the race before you referee it.** Make Operations Idempotent says: **design for the run that happens twice.** Explain the Number says: **a result you cannot explain is not yet a result.** +Earn Its Place says: **every part shows it helps, or has a dated plan to.** Together: search first, then build the complete version of the right thing. The worst outcome is building a complete version of something that already diff --git a/VERSION b/VERSION index 7a11af84d..c7cfdb7de 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -1.94.24.0 +1.94.30.0 diff --git a/agents-digest/gstack-AGENTS.md b/agents-digest/gstack-AGENTS.md index c74798cc8..01ffae367 100644 --- a/agents-digest/gstack-AGENTS.md +++ b/agents-digest/gstack-AGENTS.md @@ -1,4 +1,4 @@ -# gstack digest v1.94.24.0 — regenerate/re-copy after upgrading gstack +# gstack digest v1.94.30.0 — regenerate/re-copy after upgrading gstack Behavioral rules from gstack (https://github.com/garrytan/gstack), compressed for agent hosts without a full skill install. The full skills add workflows, diff --git a/autoplan/sections/ceo-phase.md b/autoplan/sections/ceo-phase.md index 318f3ec15..3ba3f3ca0 100644 --- a/autoplan/sections/ceo-phase.md +++ b/autoplan/sections/ceo-phase.md @@ -11,11 +11,12 @@ announcement. Dispatching a reviewer never completes its step. - Premises: accept reasonable ones (P6). Queue clearly-wrong/challenged premises as User Challenges for Phase 4: assumption, reason and cost of proceeding. The user decides there; never stop mid-pipeline. +- List each part the plan adds to gstack (skill, step, instruction, check, default) with its + evidence or owned, dated test plan; a missing one is a finding. - Alternatives: pick highest completeness (P1). If tied, pick simplest (P5). If top 2 are close → mark TASTE DECISION. - Scope expansion: in blast radius + <1d CC → approve (P2). Outside → defer to TODOS.md (P3). Duplicates → reject (P4). Borderline (3-5 files) → mark TASTE DECISION. -- All 11 review sections: run fully, auto-decide each issue, log every decision. **Required execution checklist (CEO):** @@ -38,8 +39,7 @@ Before every spec dispatch, including after each accepted spec fix, run: ```bash bun "" amend-input ceo "" "" "" "" ``` -This applies the recorded requirements and exports the complete current -`Implementation plan`. Keep returned `checkpointPath` as the amendment baseline; +Keep returned `checkpointPath` as the amendment baseline; use returned `reviewInputPath` as ``. Read that file at every returned `readRanges` offset/limit through EOF, then read the CEO scope summary in full. Reconcile dispositions, scope counts, proposal IDs and actual heading/test references diff --git a/autoplan/sections/ceo-phase.md.tmpl b/autoplan/sections/ceo-phase.md.tmpl index a4e0e1311..151a737fe 100644 --- a/autoplan/sections/ceo-phase.md.tmpl +++ b/autoplan/sections/ceo-phase.md.tmpl @@ -9,11 +9,12 @@ announcement. Dispatching a reviewer never completes its step. - Premises: accept reasonable ones (P6). Queue clearly-wrong/challenged premises as User Challenges for Phase 4: assumption, reason and cost of proceeding. The user decides there; never stop mid-pipeline. +- List each part the plan adds to gstack (skill, step, instruction, check, default) with its + evidence or owned, dated test plan; a missing one is a finding. - Alternatives: pick highest completeness (P1). If tied, pick simplest (P5). If top 2 are close → mark TASTE DECISION. - Scope expansion: in blast radius + <1d CC → approve (P2). Outside → defer to TODOS.md (P3). Duplicates → reject (P4). Borderline (3-5 files) → mark TASTE DECISION. -- All 11 review sections: run fully, auto-decide each issue, log every decision. **Required execution checklist (CEO):** @@ -36,8 +37,7 @@ Before every spec dispatch, including after each accepted spec fix, run: ```bash bun "" amend-input ceo "" "" "" "" ``` -This applies the recorded requirements and exports the complete current -`Implementation plan`. Keep returned `checkpointPath` as the amendment baseline; +Keep returned `checkpointPath` as the amendment baseline; use returned `reviewInputPath` as ``. Read that file at every returned `readRanges` offset/limit through EOF, then read the CEO scope summary in full. Reconcile dispositions, scope counts, proposal IDs and actual heading/test references diff --git a/bin/gstack-evidence b/bin/gstack-evidence index e3d2bbab1..d87f76e28 100755 --- a/bin/gstack-evidence +++ b/bin/gstack-evidence @@ -37,7 +37,9 @@ * the tail is the stdout tail then the stderr tail, so a line's position says * nothing about when it was printed across streams, and bun's own counts (on * stderr) would otherwise outrank a 10k-test run whichever of the two ran - * first. Any summary when it did not speak follows the last-summary-wins rule; + * first. Any summary when it did not speak follows the last-summary-wins rule, + * except that a complete set of `bun test --shard=i/N` processes ending the log + * is summed (GBEV-3); * anything else is "unknown". * Three states, never merged: * a count, 0 (ZERO-RUN, graded like STALE by `check`) and unknown (non-blocking). @@ -95,7 +97,7 @@ import { isUnavailable, loadPolicyFromBase, resolveBaseBranch } from "../lib/shi import { versionAtRef } from "../lib/restamp"; import { bashCommandInvocation, runScriptSync, wtreeFingerprint } from "../lib/script-invocation"; import { findCarry, laneMapping, mappingCoverage, perFileCommand } from "../lib/ci-carry"; -import { runChanged } from "../lib/run-evidence"; +import { DEFAULT_MAX_AGE_HOURS, runChanged } from "../lib/run-evidence"; import { isConfigUnavailable, loadConfigFromBase } from "../lib/pregate/config"; import { buildRunCheck } from "../lib/pregate/run-check"; @@ -146,30 +148,48 @@ const TAIL_MAX_BYTES = 64 * 1024; * Ring of the last TAIL_MAX_BYTES of each child stream, kept per stream: the * pumps interleave by arrival, so a stderr summary read while stdout still has * pipe-buffered bytes behind it would otherwise be pushed out of a shared ring. + * Lines leaving the ring that bun's shard sum reads (a "--shard=i/N: running" + * line, " N pass", " N fail", "Ran N tests across") are kept in order ahead of + * the tail, so every shard of a long sharded run still counts (GBEV-3). */ class OutputTail { private readonly decoders = { stdout: new TextDecoder("utf-8"), stderr: new TextDecoder("utf-8") }; private readonly text = { stdout: "", stderr: "" }; + private readonly kept = { stdout: [] as string[], stderr: [] as string[] }; + private trim(origin: "stdout" | "stderr", text: string): string { + const cut = text.lastIndexOf("\n", text.length - TAIL_MAX_BYTES); + if (cut < 0) return text.slice(-TAIL_MAX_BYTES); + const lines = text.slice(0, cut).split("\n").filter((l) => /^(?:--shard=\d+\/\d+: running |\s*\d+ (?:pass|fail)\s*$|Ran \d+ tests? across )/.test(strip(l))); + this.kept[origin] = [...this.kept[origin], ...lines].slice(-4096); + return text.slice(cut + 1); + } push(chunk: Uint8Array, origin: "stdout" | "stderr"): void { this.text[origin] += this.decoders[origin].decode(chunk, { stream: true }); - if (this.text[origin].length > TAIL_MAX_BYTES * 2) this.text[origin] = this.text[origin].slice(-TAIL_MAX_BYTES); + if (this.text[origin].length > TAIL_MAX_BYTES * 2) this.text[origin] = this.trim(origin, this.text[origin]); } end(): string { const tails = (["stdout", "stderr"] as const).map((origin) => { const text = this.text[origin] + this.decoders[origin].decode(); - return text.length > TAIL_MAX_BYTES ? text.slice(-TAIL_MAX_BYTES) : text; + const tail = text.length > TAIL_MAX_BYTES ? this.trim(origin, text) : text; + return [...this.kept[origin], tail].join("\n"); }); return tails.join("\n"); } } +/** The one spelling a lane label has in the ledger: run and record-ci write it, check reads it (`pnpm lint` is `pnpm_lint`, TRUT-7). */ +const ledgerLabel = (raw: string): string => raw.replace(/[^a-zA-Z0-9._-]/g, "_"); + const ANSI = /\u001B\[[0-?]*[ -/]*[@-~]/g; const strip = (l: string) => l.replace(ANSI, "").replace(/\r$/, ""); const count = (text: string, word: string): number => Number(new RegExp(`(\\d+) ${word}\\b`).exec(text)?.[1] ?? 0); /** * Bun: the last "Ran N tests across M files" block; executed = its " N pass" + - * " N fail" lines. Bun always prints that counts block (" 0 pass" / " 0 fail" + * " N fail" lines. When that block ends a `bun test --shard=i/N` process (bun's + * "--shard=i/N: running" line since the previous block), the blocks of shards + * 1/N to N/N, in that order, that end the log are summed (a sharded runner prints + * every shard, GBEV-3); any other shard set counts the last block alone. Bun always prints that counts block (" 0 pass" / " 0 fail" * even for an all-skip or empty run), so a terminal line with no " N pass" * above it is a line gstack's quiet shard runner forwarded (it forwards the * terminal line without the " N pass" line), not a bun summary: @@ -186,7 +206,9 @@ async function bunTestsRan(lines: string[]): Promise<{ at: number; ran: number } } catch { warn("shard-engine classifier not importable; using the built-in bun summary parser"); } - for (let i = lines.length - 1; i >= 0; i--) { + const blocks: { at: number; ran: number; shard?: string }[] = []; + let since = 0; + for (let i = 0; i < lines.length; i++) { if (!isTerminal(lines[i])) continue; let pass: number | null = null; let fail = 0; @@ -197,13 +219,17 @@ async function bunTestsRan(lines: string[]): Promise<{ at: number; ran: number } if (p) pass = Number(p[1]); if (f) fail = Number(f[1]); } - if (pass === null) { - if (/^Ran 0 tests? across/.test(lines[i])) return { at: i, ran: 0 }; - continue; - } - return { at: i, ran: pass + fail }; + const shard = lines.slice(since, i).map((l) => /^--shard=(\d+\/\d+): running /.exec(l)?.[1]).findLast(Boolean); + since = i + 1; + if (pass !== null) blocks.push({ at: i, ran: pass + fail, shard }); + else if (/^Ran 0 tests? across/.test(lines[i])) blocks.push({ at: i, ran: 0 }); } - return null; + const last = blocks.at(-1); + if (!last) return null; + const total = Number(last.shard?.split("/")[1] ?? 1); + const set = blocks.slice(-total); + const complete = total > 1 && set.length === total && set.every((b, k) => b.shard === `${k + 1}/${total}`); + return { at: last.at, ran: complete ? set.reduce((n, b) => n + b.ran, 0) : last.ran }; } /** @@ -428,7 +454,7 @@ async function cmdRun(argv: string[]): Promise { const commandString = cmdArgv.length === 1 ? cmdArgv[0] : cmdArgv.join(" "); const shell = cmdArgv.length === 1 ? bashCommandInvocation(cmdArgv[0]) : null; const cmdSha = sha256(commandString); - label = label.replace(/[^a-zA-Z0-9._-]/g, "_"); + label = ledgerLabel(label); // Bookkeeping context — every piece is optional; failures only warn. let paths: ReturnType | undefined; @@ -675,7 +701,8 @@ async function cmdRecordCi(argv: string[]): Promise { const sep = argv.indexOf("--"); const opts = sep < 0 ? argv : argv.slice(0, sep); const get = (name: string) => { const i = opts.indexOf(name); return i >= 0 ? opts[i + 1] : undefined; }; - const label = get("--label")?.replace(/[^a-zA-Z0-9._-]/g, "_"); + const raw = get("--label"); + const label = raw === undefined ? undefined : ledgerLabel(raw); const base = get("--base"); const command = sep < 0 ? "" : argv.slice(sep + 1).join(" "); if (!label || !base || !command || opts.length !== 4) { @@ -754,13 +781,76 @@ function cmdCarried(argv: string[]): number { const wtree = currentWtree(); const latest = new Map(); for (const r of records) latest.set(r.label, r); - const carried = [...latest.values()].filter((r) => r.source === "ci" && !!wtree && r.wtree === wtree && Array.isArray(r.runs) && typeof r.policy === "string" && r.policy.startsWith(policyRef)) + const carried = [...latest.values()].filter((r) => r.source === "ci" && !!wtree && r.wtree === wtree && Array.isArray(r.runs) && typeof r.policy === "string" && r.policy.startsWith(policyRef) + && gradeRecord(r, { maxAgeHours: DEFAULT_MAX_AGE_HOURS, allowPaths: [], wtreeNow: wtree }).verdict === "FRESH") .flatMap((r) => r.runs!.map((ref) => { const [run, attempt] = ref.split("/").map(Number); return { lane: r.label, run, attempt, tree: r.wtree! }; })); if (argv.includes("--json")) console.log(JSON.stringify(carried)); else for (const c of carried) console.log(`CI_CARRIED: ${c.lane} run=${c.run}/${c.attempt} tree=${c.tree.slice(0, 12)}`); return 0; } +/** check's verdict for one ledger row; `carried` lists a CI row only when it grades FRESH here, so a carry that never reached FRESH leaves no receipt entry. */ +function gradeRecord(latest: EvidenceRecord, o: { maxAgeHours?: number; expectCmd?: string; allowPaths: string[]; wtreeNow: string | undefined }): { verdict: "FRESH" | "STALE" | "ZERO-RUN"; reason: string } { + const { maxAgeHours, expectCmd, allowPaths, wtreeNow } = o; + const testsRan = typeof latest.tests_ran === "number" || latest.tests_ran === "n/a" ? latest.tests_ran : "unknown"; + const changedDuringRun = Array.isArray(latest.wtree_changed) ? latest.wtree_changed.filter((p): p is string => typeof p === "string" && p !== "" && !/[\u0000-\u001f\u007f]/.test(p)).slice(0, 5) : []; + let verdict: "FRESH" | "STALE" | "ZERO-RUN" = "FRESH"; + let reason = ""; + if (latest.exit !== 0) { + verdict = "STALE"; + reason = "recorded run failed"; + } else if (testsRan === 0) { + // Zero-run green: exit 0 with nothing executed (all skipped, empty + // selection, "No tests found"). Graded like STALE under its own label; + // "unknown" (no recognised runner summary) stays non-blocking. + verdict = "ZERO-RUN"; + reason = "runner reported 0 executed tests"; + } else if (maxAgeHours !== undefined) { + const ageMs = Date.now() - Date.parse(latest.ts); + if (!(ageMs >= 0 && ageMs <= maxAgeHours * 3600 * 1000)) { + verdict = "STALE"; + reason = `older than ${maxAgeHours}h`; + } + } + if (verdict === "FRESH" && expectCmd !== undefined && sha256(expectCmd) !== latest.cmd_sha256) { + verdict = "STALE"; + reason = "command changed (cmd_sha256 mismatch)"; + } + if (verdict === "FRESH") { + // Content binding: identical working-tree fingerprint, or a diff confined + // to the allow-list. Any git failure (gc'd tree, not a repo) → STALE — + // never an error into the calling flow. + if (!latest.wtree || !/^[0-9a-f]{40}$/.test(latest.wtree) || !wtreeNow) { + // Stored fingerprints are re-validated before reaching git argv — a + // forged/corrupt ledger line must degrade, never inject options. + verdict = "STALE"; + reason = !latest.wtree + ? changedDuringRun.length + ? `content changed during the run: ${changedDuringRun.join(", ")}` + : "record has no content fingerprint" + : !/^[0-9a-f]{40}$/.test(latest.wtree) + ? "record has malformed fingerprint" + : "current fingerprint unavailable"; + } else if (latest.wtree !== wtreeNow) { + const diff = git(["diff", "--name-only", latest.wtree, wtreeNow]); + if (diff === undefined) { + verdict = "STALE"; + reason = "content changed (fingerprint diff unavailable)"; + } else { + const changed = diff.split("\n").map((s) => s.trim()).filter(Boolean); + const outside = changed.filter((f) => !allowPaths.some((a) => f === a || f.startsWith(a.replace(/\/$/, "") + "/"))); + if (changed.length === 0 || outside.length === 0) { + reason = changed.length ? `diff confined to allow-paths (${changed.length} file(s))` : ""; + } else { + verdict = "STALE"; + reason = `content changed: ${outside.slice(0, 5).join(", ")}${outside.length > 5 ? ", ..." : ""}`; + } + } + } + } + return { verdict, reason }; +} + function cmdCheck(argv: string[]): number { // Parse: repeated --label, each optionally followed (anywhere later) by its // own --expect-cmd; pairing is positional — an --expect-cmd binds to the most @@ -771,7 +861,7 @@ function cmdCheck(argv: string[]): number { let allowPaths: string[] = []; for (let i = 0; i < argv.length; i++) { const a = argv[i]; - if (a === "--label") wanted.push({ label: argv[++i] ?? "" }); + if (a === "--label") wanted.push({ label: ledgerLabel(argv[++i] ?? "") }); else if (a === "--expect-cmd") { if (wanted.length === 0) { console.error("gstack-evidence: --expect-cmd requires a preceding --label"); @@ -820,63 +910,9 @@ function cmdCheck(argv: string[]): number { continue; } const testsRan = typeof latest.tests_ran === "number" || latest.tests_ran === "n/a" ? latest.tests_ran : "unknown"; - const changedDuringRun = Array.isArray(latest.wtree_changed) ? latest.wtree_changed.filter((p): p is string => typeof p === "string" && p !== "" && !/[\u0000-\u001f\u007f]/.test(p)).slice(0, 5) : []; const ci = latest.source === "ci" && Array.isArray(latest.runs) ? ` source=ci runs=${latest.runs.filter((s) => /^\d+\/\d+$/.test(s)).join(",")}` : ""; const detail = `label=${label} exit=${latest.exit} tests_ran=${testsRan}${latest.tests_unit === "files" ? " unit=files" : ""} ts=${latest.ts}${latest.log_path ? ` log=${latest.log_path}` : ""}${ci}`; - let verdict: "FRESH" | "STALE" | "ZERO-RUN" = "FRESH"; - let reason = ""; - if (latest.exit !== 0) { - verdict = "STALE"; - reason = "recorded run failed"; - } else if (testsRan === 0) { - // Zero-run green: exit 0 with nothing executed (all skipped, empty - // selection, "No tests found"). Graded like STALE under its own label; - // "unknown" (no recognised runner summary) stays non-blocking. - verdict = "ZERO-RUN"; - reason = "runner reported 0 executed tests"; - } else if (maxAgeHours !== undefined) { - const ageMs = Date.now() - Date.parse(latest.ts); - if (!(ageMs >= 0 && ageMs <= maxAgeHours * 3600 * 1000)) { - verdict = "STALE"; - reason = `older than ${maxAgeHours}h`; - } - } - if (verdict === "FRESH" && expectCmd !== undefined && sha256(expectCmd) !== latest.cmd_sha256) { - verdict = "STALE"; - reason = "command changed (cmd_sha256 mismatch)"; - } - if (verdict === "FRESH") { - // Content binding: identical working-tree fingerprint, or a diff confined - // to the allow-list. Any git failure (gc'd tree, not a repo) → STALE — - // never an error into the calling flow. - if (!latest.wtree || !/^[0-9a-f]{40}$/.test(latest.wtree) || !wtreeNow) { - // Stored fingerprints are re-validated before reaching git argv — a - // forged/corrupt ledger line must degrade, never inject options. - verdict = "STALE"; - reason = !latest.wtree - ? changedDuringRun.length - ? `content changed during the run: ${changedDuringRun.join(", ")}` - : "record has no content fingerprint" - : !/^[0-9a-f]{40}$/.test(latest.wtree) - ? "record has malformed fingerprint" - : "current fingerprint unavailable"; - } else if (latest.wtree !== wtreeNow) { - const diff = git(["diff", "--name-only", latest.wtree, wtreeNow]); - if (diff === undefined) { - verdict = "STALE"; - reason = "content changed (fingerprint diff unavailable)"; - } else { - const changed = diff.split("\n").map((s) => s.trim()).filter(Boolean); - const outside = changed.filter((f) => !allowPaths.some((a) => f === a || f.startsWith(a.replace(/\/$/, "") + "/"))); - if (changed.length === 0 || outside.length === 0) { - reason = changed.length ? `diff confined to allow-paths (${changed.length} file(s))` : ""; - } else { - verdict = "STALE"; - reason = `content changed: ${outside.slice(0, 5).join(", ")}${outside.length > 5 ? ", ..." : ""}`; - } - } - } - } + const { verdict, reason } = gradeRecord(latest, { maxAgeHours, expectCmd, allowPaths, wtreeNow }); console.log(`EVIDENCE: ${verdict} ${detail}${reason ? ` reason=${reason}` : ""}`); if (verdict !== "FRESH") allFresh = false; } diff --git a/bin/gstack-inventory b/bin/gstack-inventory new file mode 100755 index 000000000..a8f770afc --- /dev/null +++ b/bin/gstack-inventory @@ -0,0 +1,54 @@ +#!/usr/bin/env -S bun --no-env-file --config=/dev/null +// gstack-inventory — every gstack part, its load per run and its evidence (ETHOS.md: 9. Earn Its Place). Logic: lib/inventory.ts. +import { spawnSync } from "node:child_process"; +import { existsSync, writeFileSync } from "node:fs"; +import { join, resolve } from "node:path"; +import { buildInventory, checkInventory, DOC_PATH, installReason, LEDGER_PATH, renderInventory } from "../lib/inventory"; + +const USAGE = `Usage: gstack-inventory [--root ] [--check | --json] + gstack-inventory --help + +Joins the evidence ledger (${LEDGER_PATH}) with gstack-context-bill's sizes and writes ${DOC_PATH}: +parts ranked by load per run, each part's evidence status, a "measure next" order (largest untested +first), shared-part sizes, paid dispatches per run for /ship and /autoplan, and the key list. +--check validates the ledger (every key has one row; each row has its status's fields), the quoted +dispatch sources and the shared-part files, and writes nothing. --json prints the inventory. +The root defaults to the git repo you run it in; it never writes into a gstack install. +Exit 0 when clean; 1 with one ": ()" line per problem; 2 on a bad argument.`; + +const args = process.argv.slice(2); +if (args.includes("--help") || args.includes("-h")) { console.log(USAGE); process.exit(0); } +const rootAt = args.indexOf("--root"); +const rootArg = rootAt >= 0 ? args[rootAt + 1] : undefined; +const unknown = args.find((a, i) => !["--check", "--json", "--root"].includes(a) && !(rootAt >= 0 && i === rootAt + 1)); +if (unknown !== undefined || (rootAt >= 0 && (!rootArg || rootArg.startsWith("--"))) || (args.includes("--check") && args.includes("--json"))) { + console.error(unknown !== undefined ? `gstack-inventory: unknown argument '${unknown}'; valid: --root --check --json --help` : USAGE); + process.exit(2); +} +const top = spawnSync("git", ["rev-parse", "--show-toplevel"], { encoding: "utf8", timeout: 10_000 }); +const root = resolve(rootArg ?? (top.status === 0 ? top.stdout.trim() : process.cwd())); +if (rootArg === undefined && !existsSync(join(root, LEDGER_PATH))) { + console.error(`gstack-inventory: ${root} is not a gstack checkout (no ${LEDGER_PATH}); run it in your gstack checkout or pass --root `); + process.exit(2); +} +const report = (problems: { key: string; code: string; message: string }[]) => { + for (const p of problems) console.error(`gstack-inventory: ${p.key}: ${p.message} (${p.code})`); +}; +if (args.includes("--check")) { + const checked = checkInventory(root); + report(checked.problems); + if (checked.problems.length) process.exit(1); + console.log(`INVENTORY: ok keys=${checked.keys.length} ledger=${LEDGER_PATH} dispatch_sources=${checked.dispatches.length}`); + process.exit(0); +} +const inv = buildInventory(root); +report(inv.problems); +if (args.includes("--json")) { console.log(JSON.stringify(inv, null, 2)); process.exit(inv.problems.length ? 1 : 0); } +if (inv.problems.length) { console.error(`gstack-inventory: ${DOC_PATH} not written; fix the problems above, then rerun`); process.exit(1); } +const installed = installReason(root); +if (installed) { + console.error(`gstack-inventory: refusing to write ${DOC_PATH} into ${root}: it is ${installed}, which gstack-upgrade owns; run it in your gstack checkout or pass --root `); + process.exit(2); +} +writeFileSync(join(root, DOC_PATH), renderInventory(inv)); +console.log(`INVENTORY: wrote ${DOC_PATH} keys=${inv.keys.length} commit=${inv.commit}`); diff --git a/bin/gstack-pregate b/bin/gstack-pregate index c14279033..3fdf66be3 100755 --- a/bin/gstack-pregate +++ b/bin/gstack-pregate @@ -2,7 +2,7 @@ /** * gstack-pregate — cheap checks before anything paid or remote (plan C8). * - * gstack-pregate [run] [--stage preflight|tests|all] [--only ] [--base ] + * gstack-pregate [run] [--stage preflight|tests|guards|all] [--only ] [--base ] * [--allow-repo-commands] [--policy-from base|head] [--out ] * [--timeout ] [--stall-timeout ] [--remote --spend-cap ] [--run ] [--from-run ] [--no-ci-carry] [--explain] [--json] * gstack-pregate verify [--out ] [--json] publication check: current tree, no fail/incomplete, obligations cleared @@ -33,7 +33,7 @@ import { PREGATE_CONFIG_PATH, explainConfig, loadConfigFromHead } from '../lib/p import { describeDraft, draftConfig } from '../lib/pregate/init-draft'; import { buildRunCheck } from '../lib/pregate/run-check'; import { buildContext, defaultUserConfigPath, describeRefusal, loadedDotenvFiles } from '../lib/pregate/context'; -import { CHECKS, CHECK_IDS, PREGATE_OUT_REL, PREGATE_TIER2, clearDecision, clearObligation, readPregateFile, renderTable, runPregate, verifyPregate, writePregateFile } from '../lib/pregate/index'; +import { CHECKS, CHECK_IDS, PREGATE_GUARDS_OUT_REL, PREGATE_OUT_REL, PREGATE_TIER2, PREPUSH_CHECK_IDS, clearDecision, clearObligation, readPregateFile, renderTable, runPregate, verifyPregate, writePregateFile } from '../lib/pregate/index'; import { describeLane, discoverLanes } from '../lib/pregate/workflows'; import { REMOTE_GATE_LANE } from '../lib/pregate/checks/lanes'; import { describeSubstitution } from '../lib/pregate/remote-receipt'; @@ -75,7 +75,8 @@ const HELP = `gstack-pregate — cheap checks before anything paid or remote (do a fresh tree receipt, each lane's reuse verdict, migration collisions (base branch and open PRs), then REGATE: needed|not-needed (docs/pregate.md#regate); \`gstack-pregate --against \` is the same verb; exit 1 = needed - --stage preflight|tests|all default all + --stage preflight|tests|guards|all default all; guards is the pre-push housekeeping run (docs/pregate.md#prepush): + only ${PREPUSH_CHECK_IDS.join(' and ')}, each for the files the diff touched, written to ${PREGATE_GUARDS_OUT_REL} --only restrict to these checks (${CHECK_IDS.join(', ')}; tier 2 later: ${PREGATE_TIER2.join(', ')}) --base a branch on origin, not a commit (default: origin/HEAD's branch unless it is the checked-out one, else the PR's base, origin/main, origin/master; lib/default-base.ts) @@ -151,10 +152,11 @@ function repoRoot(): string | null { function outPath(root: string, rest: string[]): string { const o = flag(rest, '--out'); return o ? path.resolve(o) : path.join(root, PREGATE_OUT_REL); } async function cmdRun(root: string, rest: string[], json: boolean): Promise { - const stage = (flag(rest, '--stage') ?? 'all') as 'preflight' | 'tests' | 'all'; - if (!['preflight', 'tests', 'all'].includes(stage)) { process.stderr.write('gstack-pregate: --stage needs preflight, tests or all\n'); return EXIT.usage; } + const stage = (flag(rest, '--stage') ?? 'all') as 'preflight' | 'tests' | 'guards' | 'all'; + if (!['preflight', 'tests', 'guards', 'all'].includes(stage)) { process.stderr.write('gstack-pregate: --stage needs preflight, tests, guards or all\n'); return EXIT.usage; } + if (stage === 'guards' && (flag(rest, '--only') !== undefined || rest.includes('--remote'))) { process.stderr.write(`gstack-pregate: --stage guards runs only ${PREPUSH_CHECK_IDS.join(' and ')}; drop --only and --remote\n`); return EXIT.usage; } const fromRun = flag(rest, '--from-run'); - if (fromRun !== undefined && stage === 'preflight') { process.stderr.write('gstack-pregate: --from-run applies to the tests stage (--stage tests or all)\n'); return EXIT.usage; } + if (fromRun !== undefined && (stage === 'preflight' || stage === 'guards')) { process.stderr.write('gstack-pregate: --from-run applies to the tests stage (--stage tests or all)\n'); return EXIT.usage; } const only = flag(rest, '--only')?.split(',').map(s => s.trim()).filter(Boolean); const unknown = (only ?? []).filter(id => !CHECK_IDS.includes(id)); if (unknown.length) { process.stderr.write(`gstack-pregate: unknown check(s) ${unknown.join(', ')}${unknown.some(u => (PREGATE_TIER2 as readonly string[]).includes(u)) ? ' (tier 2, not in this gstack)' : ''}; known: ${CHECK_IDS.join(', ')}\n`); return EXIT.usage; } @@ -197,7 +199,7 @@ async function cmdRun(root: string, rest: string[], json: boolean): Promise" trailer; DUE means the authored file grew over 10% since then. In a gstack checkout, EVAL TASKS names test/fixtures/skill-evals//tasks.json (or none) and -EVAL_TASKS_INVALID flags a file that breaks docs/designs/SKILL_EVAL_CONTRACT.md's task format. +EVAL_TASKS_INVALID flags a file that breaks docs/designs/SKILL_EVAL_CONTRACT.md's task format, and +EVIDENCE prints the skill's row in docs/inventory/evidence.json: its status and source, plan or reason. A name must match exactly; pass a path when it names more than one skill. Exit 0 on a report, 2 on SKILL_NOT_FOUND, SKILL_AMBIGUOUS or SKILL_MD_MISSING (each with a fix line).`; diff --git a/capy/skill/gstack/factory.md b/capy/skill/gstack/factory.md index 7e472397c..3a334a6bd 100644 --- a/capy/skill/gstack/factory.md +++ b/capy/skill/gstack/factory.md @@ -1,17 +1,17 @@ -# How we work: the gstack software factory on Capy +# How we work: gstack's factory on Capy -What seven Captain crews converged on (2026-10-29). Your crew's CREW.md and recorded approvals decide who may merge, update a base, spend or touch production; urgency changes priority, never permissions or gates. With no Captain, ask the person and never merge without their word. Story: capy/README.md; examples: your org Drive's software-factory/README.md, if any. +What seven Captain crews agreed on. Your crew's CREW.md and recorded approvals decide who may merge, update a base, spend or touch production; urgency changes priority, never permissions or gates. With no Captain, ask the person; never merge without their word. Story: capy/README.md; examples: org Drive software-factory/README.md. 1. **Plan, then a yes.** New functionality starts with /autoplan sized to the work; the build waits for "do it". [SKILL.md: Plan, approve, ship] 2. **Someone else merges.** Run /ship to a receipt on your final pushed head, send one READY quoting gstack's verify output verbatim, and stop. The merge owner merges on green and assigns versions. [docs/captain.md: The READY contract] 3. **Block only on the floor.** A finding blocks if it would fail a test, break production, or lose data or secrets; the rest goes in the PR body or next wave. [docs/review-policy.md] 4. **Fix waves.** When main goes red, hold /ship while one owner finds the root cause; bring main in once, as your rules allow. Fold deferred findings and test follow-ups into one PR. [docs/triage.md] -5. **Severe first, same gates.** A severe user-facing bug jumps the queue; the plan can be short; receipt, checks and merge owner stay. -6. **Trade findings, not small talk.** Another crew's finding goes to its Captain, through yours, with the evidence. +5. **Severe first, same gates.** A severe user-facing bug jumps the queue with a short plan; receipt, checks and merge owner stay. +6. **Trade findings, not small talk.** Another crew's finding goes to its Captain, through yours, with evidence. 7. **Measure before you act.** Run the real system on the merged tree you ship; a real run outranks a reviewed plan. Each number names its run, commit or uncapped query; say what you did not verify. -8. **Evals decide, and losses get reported.** Preregister bars, cap and stop rule before paying [/eval-plan]. Cheap proof (a keyless fake, a smoke) before the paid run. Use controls (no-skill and placebo arms, clean decoys). Report use and pass apart; quote spend from the meter. -9. **Flakes: one rerun, a ledger, a daily fix.** Only the rerun the policy allows, never a harness or infra red; a second red is real; no test is loosened. [docs/captain.md: The daily flaky pass] +8. **Evals decide, and losses get reported.** Preregister bars, cap and stop rule before paying [/eval-plan]. Cheap proof (a keyless fake, a smoke) first. Controls: no-skill and placebo arms, clean decoys. Report use and pass apart; quote spend from the meter. A new part needs proof it helps or a dated test plan [ETHOS.md: 9. Earn Its Place]. +9. **Flakes: one rerun, a ledger, a daily fix.** Only the policy's rerun, never for a harness or infra red; a second red is real; never loosen a test. [docs/captain.md: The daily flaky pass] 10. **Quiet.** Message on a decision, a blocker, a READY or a finished phase. A broadcast needing no reply goes in the Captain's notes; never reply just to acknowledge. -11. **Ask once, plainly.** One ask per decision, explained like the owner is five: your Captain's name, then a lettered line each: "A: hit problem p, which happens when h. We could do 1) x, 2) y or 3) z. Recommendation x because why. A1 to take recommendation." "do it" takes every recommendation; "A2" takes option 2. Owner tasks: the exact link (in Capy: path and field), numbered simple steps, the reply word. Never re-ask a standing rule; check what you can yourself. [docs/captain.md: The ask format] +11. **Ask once, plainly.** One ask per decision, explained like the owner is five: your Captain's name, then a lettered line each: "A: hit problem p, which happens when h. We could do 1) x, 2) y or 3) z. Recommendation x because why. A1 to take recommendation." "do it" takes every recommendation; "A2" takes option 2. Owner tasks: the exact link (in Capy: path and field), numbered steps, the reply word. Never re-ask a standing rule; check what you can yourself. [docs/captain.md: The ask format] 12. **Fresh threads, closed out.** One thread per unit of work (crew threads ask their Captain); past the context limit, hand off. Its Captain archives only its crew's threads; threads outside every crew are the owner's, even its automation's. Size reviews to the diff; read the token counter. [docs/ship-budget.md] 13. **The owner's model preference.** `gstack-models resolve --role agent` picks agent models, except cross-family voices and eval subjects. [docs/model-policy.md: Your model preference] diff --git a/changelog.d/gsta258-c-review-depth.md b/changelog.d/gsta258-c-review-depth.md deleted file mode 100644 index 32f06c39e..000000000 --- a/changelog.d/gsta258-c-review-depth.md +++ /dev/null @@ -1,15 +0,0 @@ -**/ship sends more diffs to full review: spend-binding code, eval scorers, a repo's own risk paths and docs that print secrets now count as risk, and a lane can declare a class the matchers miss (GSTA-258 build C).** Today's reviewer panel stays the default. The one-reader shape for non-risk code ships as an opt-in (`review.panel risk`) because its no-loss replay didn't pass: with only the diff and no repository, the reader found 2 of 68 non-risk blockers, so the replay is being redone with readers working in a checkout. A repo with platform review on can skip /ship's readers on plain docs. - -### Added -- **A `review` key in `.gstack/ship-policy.json`**, read from the base branch only (needs `min_gstack_version` at the release that adds it): `panel` (`all` by default, today's panel; `risk` or `off` opt in to one reader), `reader_model` (`lane` by default), `platform` (`off` by default) and `risk_paths` (globs of code paths that count as risk class `policy`). -- **`gstack-review-plan` prints `REVIEW_MODE:`** and, under `review.platform on`, `PLATFORM_REVIEW:`. `DISPATCH:` lines name the reader's `model=` and `classes=`. New flags: `--fix-checks ` and `--pr `. -- **The platform depth.** Under `review.platform on`, a docs or tests diff with no numeric, results or prereg claims, whose PR carries capy-ai[bot]'s review at the exact head, plans `NEXT: platform` with a planned `SKIP:` for every reader. Step 11.5 accepts it, and `gstack-ship-receipt write --platform-review ` records `review_depth: platform` with that review's id and head. It never applies to runtime code, and never where platform review is off. -- **New risk classes**: `eval` (scorer, judge and grader paths) and `policy` (`review.risk_paths`). A docs line that can print a credential (an `Authorization:` header, a bearer value, a key-shaped literal, `mcp get`) is security risk (gbrain#8417). -- **The receipt records `review_depth.mode`** (lean, panel or platform). -- **Declared risk classes** (GST-1129). `gstack-review-plan --declare-risk ` and `gstack-ship-receipt write --declare-risk ` add classes the matchers missed. A declaration only raises the depth, the plan prints `RISK_DECLARED:`, and the receipt keeps `review_depth.declared` beside `review_depth.detected`. -- **Code that reserves, binds, leases or settles spend is money** (GST-1129): `bindCheck(ledger, …)`, `reserveSpend` or `bindCap` in a code file now plans full depth. gstack-evals#170 got its full review only because an unrelated `UBICLOUD_API_KEY` line was in the diff. - -### Changed -- **Opt-in one-reader review** (`review.panel risk`): non-risk code gets one native adversarial reader at the lane's reasoning, carrying the code checklists and the Red Team checklist, and risk class keeps the full panel. One fix-check follows a fixed blocker. It isn't the default until its no-loss replay passes. -- **Fewer false risk triggers** (harness-evals#231): committed `*.sqlite` and other binary data files count as data, never code; JSON, YAML and other config files are judged by their path, so a `lease_usd` value no longer means money; analysis scripts under `docs/`, `results/` or `benchmarks/` are judged by their path. -- The outside voice reads its diff from a file the brief names with its sha256, instead of a diff pasted into the prompt. Past 2,000,000 bytes it reads a path-scoped slice whose brief lists the files it left out, instead of stopping the pass. diff --git a/docs/TESTING_INTERNALS.md b/docs/TESTING_INTERNALS.md index 38ce1e467..f4cc39a74 100644 --- a/docs/TESTING_INTERNALS.md +++ b/docs/TESTING_INTERNALS.md @@ -1871,6 +1871,24 @@ own attempts. The arm gets: - the same budget treatment: it is priced and reserved in the pilot like every other arm. +**Masking.** On Capy, the owner's user-scope instructions tell every session to +load the user-drive gstack skill, and eval run agents inherit them: in one +checkpoint (24 runs, 3 arms) every run in every arm tried to read that skill in +its first minute. So `## No-gstack arm` also needs three lines, and `prereg +--check` refuses a missing, blank or `none` one (`PREREG_NO_GSTACK_ARM`): + +- `masking:` an empty directory bind-mounted over `~/.capy/drive/user-garry-tan` + and `~/.capy/drive/org-gstack-gbrain` before the run agent starts, or a + container that sees no Capy drive; +- `install:` no gstack on the arm's machine (`~/.local/share/gstack`, + `~/.claude/skills`), checked before the run; +- `audit:` a per-arm audit of drive and skill reads. + +An arm that read a gstack skill file is contaminated and gets zero credit. A +no-gstack result that ran before this rule without a mask is not evidence for +an R48 verdict until it is re-checked; the evidence ledger records it under +`prior` with `unmasked` ([docs/inventory.md](inventory.md)). + **Reporting.** The no-gstack result is one row beside the variants in every results table and every verdict, including when it wins. A comparison that drops the row has not reported its result. `gstack-eval-plan results --check diff --git a/docs/inventory.md b/docs/inventory.md new file mode 100644 index 000000000..5cef90170 --- /dev/null +++ b/docs/inventory.md @@ -0,0 +1,249 @@ +# gstack inventory: every part, its load per run and its evidence + +Generated by `bin/gstack-inventory` from [evidence.json](inventory/evidence.json), the hand-kept ledger, and lib/context-bill.ts. Measured at commit cd67c41d8 (2026-11-02). This is a dated snapshot: rerun `bin/gstack-inventory` to refresh it; `bin/gstack-inventory --check` validates the ledger. The rule: [ETHOS.md: 9. Earn Its Place](../ETHOS.md#9-earn-its-place). + +Token counts are offline estimates (estimate: calibrated bytes/token per content class, about ±40%), not billed tokens. + +| Status | Parts | +|---|---| +| helps | 0 | +| no-effect | 1 | +| hurts | 0 | +| untested, with a plan | 17 | +| untested, plan none | 48 | +| safety | 5 | + +## Parts by load per run + +Load per run: a skill's SKILL.md plus the files it must read on every use (`perInvocationTokens` in lib/context-bill.ts); a shared part's files. Tier 2+ skills also read the shared preamble once per session (its own row). On demand: the skill's other sections/ and references/ files, read when a step asks for them; files a shared row also counts, such as /ship's review sections, appear in both. Always-on: frontmatter every session's skill scanner reads. + +| Rank | Part | Kind | Load per run (tokens) | On demand (tokens) | Always-on (tokens) | Loads | Status | Source, plan or reason | +|---|---|---|---|---|---|---|---|---| +| 1 | ship-review-panel | shared | 22,830 | n/a | n/a | every /ship run, by depth | untested | plan: GEVA-2 registry row R56-1 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered) (grandfathered) | +| 2 | design-review | skill | 20,159 | 7,778 | 101 | every use | untested | plan: GEVA-2 registry row R56-9 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered) (grandfathered) | +| 3 | ship | skill | 14,587 | 69,678 | 91 | every use | untested | plan: GEVA-2 registry row R56-4 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered) (grandfathered) | +| 4 | plan-ceo-review | skill | 14,394 | 21,203 | 313 | every use | untested | plan: GEVA-2 registry row R56-5 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered) (grandfathered) | +| 5 | review | skill | 13,990 | 41,146 | 75 | every use | untested | plan: R2 (GEVA-2, 2026-11-02): gstack-evals wiki/plans/2026-11-02-geva-122-r2-amendment (#129, main eae6efc), told /review vs a no-gstack arm, Opus 5.5, preset on both arms (grandfathered) | +| 6 | devex-review | skill | 13,911 | 0 | 74 | every use | untested | plan: GEVA-2 registry row R56-11 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered) (grandfathered) | +| 7 | plan-design-review | skill | 13,351 | 12,986 | 79 | every use | untested | plan: GEVA-2 registry row R56-7 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered) (grandfathered) | +| 8 | retro | skill | 12,257 | 1,514 | 225 | every use | untested | plan: GEVA-2 registry row R56-12 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered) (grandfathered) | +| 9 | plan-devex-review | skill | 11,104 | 20,474 | 80 | every use | untested | plan: GEVA-2 registry row R56-6 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered) (grandfathered) | +| 10 | autoplan-phases | shared | 10,709 | n/a | n/a | every /autoplan run, by scope | untested | plan: folded into GEVA-2 registry row R56-3 (autoplan); proposed, not yet preregistered (grandfathered) | +| 11 | office-hours | skill | 10,424 | 23,078 | 280 | every use | untested | plan: none (grandfathered) | +| 12 | autoplan | skill | 10,288 | 18,701 | 394 | every use | untested | plan: GEVA-2 registry row R56-3 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered) (grandfathered) | +| 13 | plan-eng-review | skill | 9,837 | 25,963 | 74 | every use | untested | plan: GEVA-2 registry row R56-2 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered) (grandfathered) | +| 14 | design-consultation | skill | 9,557 | 10,864 | 282 | every use | untested | plan: GEVA-2 registry row R56-10 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered) (grandfathered) | +| 15 | setup-gbrain | skill | 9,099 | 10,921 | 106 | every use | untested | plan: none (grandfathered) | +| 16 | codex | skill | 8,991 | 10,358 | 68 | every use | untested | plan: none (grandfathered) | +| 17 | sync-gbrain | skill | 8,863 | 0 | 85 | every use | untested | plan: none (grandfathered) | +| 18 | design-shotgun | skill | 8,470 | 1,208 | 248 | every use | untested | plan: none (grandfathered) | +| 19 | plan-tune | skill | 8,377 | 0 | 107 | every use | untested | plan: none (grandfathered) | +| 20 | spec | skill | 8,305 | 7,759 | 92 | every use | untested | plan: none (grandfathered) | +| 21 | browse | skill | 8,125 | 5,597 | 81 | every use | untested | plan: none (grandfathered) | +| 22 | design-html | skill | 8,096 | 5,844 | 82 | every use | untested | plan: none (grandfathered) | +| 23 | land-and-deploy | skill | 7,652 | 16,188 | 63 | every use | untested | plan: GEVA-2 registry row R56-8 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered) (grandfathered) | +| 24 | benchmark | skill | 7,430 | 961 | 72 | every use | untested | plan: none (grandfathered) | +| 25 | catalog | shared | 7,291 | n/a | 7,291 | every session | untested | plan: Run D (GEVA-2, 2026-11-02): gstack-evals wiki/plans/2026-11-01-geva-125-untold-discovery-prereg (#125, main 9820b14), untold gstack vs placebo vs no gstack, preset vs stub, Opus 5.5, GPT-6.1 Sol and particle-1; also measures /investigate's description (grandfathered) | +| 26 | investigate | skill | 7,086 | 3,729 | 490 | every use | no-effect | sources: GST-814 (HARN-1, 2026-11-02), org-gstack-gbrain handoffs/HARN-14/hard-follow-2026-11/decision-rule.md; R48 CUT-CANDIDATE | +| 27 | canary | skill | 7,034 | 0 | 66 | every use | untested | plan: none (grandfathered) | +| 28 | qa | skill | 6,951 | 13,877 | 74 | every use | untested | plan: none (grandfathered) | +| 29 | scrape | skill | 6,647 | 0 | 80 | every use | untested | plan: none (grandfathered) | +| 30 | captain | skill | 6,522 | 7,856 | 127 | every use | untested | plan: none (grandfathered) | +| 31 | preamble | shared | 5,743 | n/a | n/a | every tier 2+ skill run, inside that skill's load | untested | plan: I1 ceremony cut (GEVA-2, 2026-11-02): gstack-evals wiki/plans/2026-11-01-geva-108-ceremony-cut-prereg (#122, main 409ff47) with its G3 amendment (#128), preset, Opus 5.5 and particle-1 (grandfathered) | +| 32 | gstack-upgrade | skill | 5,726 | 0 | 59 | every use | untested | plan: none (grandfathered) | +| 33 | context-restore | skill | 5,715 | 1,734 | 84 | every use | untested | plan: none (grandfathered) | +| 34 | document-generate | skill | 5,466 | 0 | 110 | every use | untested | plan: none (grandfathered) | +| 35 | ios-qa | skill | 5,368 | 1,378 | 84 | every use | untested | plan: none (grandfathered) | +| 36 | skillify | skill | 5,317 | 0 | 80 | every use | untested | plan: none (grandfathered) | +| 37 | capy-skill | shared | 5,309 | n/a | n/a | every Capy session | untested | plan: none (grandfathered) | +| 38 | health | skill | 5,287 | 0 | 68 | every use | untested | plan: none (grandfathered) | +| 39 | setup-deploy | skill | 5,216 | 0 | 73 | every use | untested | plan: none (grandfathered) | +| 40 | qa-only | skill | 5,205 | 3,460 | 68 | every use | untested | plan: none (grandfathered) | +| 41 | eval-plan | skill | 5,021 | 0 | 108 | every use | untested | plan: none (grandfathered) | +| 42 | gstack | skill | 4,951 | n/a | 59 | every use | untested | plan: none (grandfathered) | +| 43 | pair-agent | skill | 4,811 | 0 | 63 | every use | untested | plan: none (grandfathered) | +| 44 | make-pdf | skill | 4,730 | 1,767 | 65 | every use | untested | plan: none (grandfathered) | +| 45 | context-save | skill | 4,637 | 0 | 65 | every use | untested | plan: none (grandfathered) | +| 46 | cso | skill | 4,456 | 4,759 | 95 | every use | safety | reason: security: supported security findings with explicit coverage; kept on its security reason (GEVA-2 marks it exempt; GStack Minder may reclassify it) | +| 47 | open-gstack-browser | skill | 4,429 | 0 | 74 | every use | untested | plan: none (grandfathered) | +| 48 | design-motion | skill | 4,295 | 4,995 | 86 | every use | untested | plan: none (grandfathered) | +| 49 | test-audit | skill | 4,201 | 0 | 82 | every use | untested | plan: none (grandfathered) | +| 50 | document-release | skill | 4,049 | 10,260 | 72 | every use | untested | plan: none (grandfathered) | +| 51 | diagram | skill | 3,895 | 0 | 112 | every use | untested | plan: none (grandfathered) | +| 52 | benchmark-models | skill | 3,822 | 0 | 77 | every use | untested | plan: none (grandfathered) | +| 53 | ubicloud | skill | 3,593 | 0 | 83 | every use | untested | plan: none (grandfathered) | +| 54 | modernize | skill | 3,429 | 0 | 106 | every use | untested | plan: none (grandfathered) | +| 55 | setup-browser-cookies | skill | 3,184 | 0 | 79 | every use | untested | plan: none (grandfathered) | +| 56 | deslop-shared-libs | skill | 3,100 | 0 | 64 | every use | untested | plan: none (grandfathered) | +| 57 | landing-report | skill | 2,971 | 0 | 71 | every use | untested | plan: none (grandfathered) | +| 58 | ship-docs-audit | shared | 2,730 | n/a | n/a | every /ship run | untested | plan: folded into GEVA-2 registry row R56-4 (ship); proposed, not yet preregistered (grandfathered) | +| 59 | verify | skill | 2,679 | 0 | 91 | every use | untested | plan: none (grandfathered) | +| 60 | ios-design-review | skill | 2,367 | 0 | 76 | every use | untested | plan: none (grandfathered) | +| 61 | ios-sync | skill | 2,240 | 0 | 90 | every use | untested | plan: none (grandfathered) | +| 62 | ios-clean | skill | 2,214 | 0 | 86 | every use | untested | plan: none (grandfathered) | +| 63 | write-skill | skill | 2,158 | 795 | 100 | every use | untested | plan: none (grandfathered) | +| 64 | ios-fix | skill | 2,096 | 0 | 69 | every use | untested | plan: none (grandfathered) | +| 65 | learn | skill | 1,989 | 0 | 66 | every use | untested | plan: none (grandfathered) | +| 66 | careful | skill | 1,126 | 0 | 171 | every use | safety | reason: safety guard: warns before destructive commands (rm -rf, DROP TABLE, force-push); kept on that reason, an average score cannot see the rare bad run it stops | +| 67 | freeze | skill | 1,043 | 0 | 234 | every use | safety | reason: safety guard: hard-blocks edits outside one directory; kept on that reason | +| 68 | guard | skill | 908 | 0 | 348 | every use | safety | reason: safety guard: careful and freeze together; kept on that reason | +| 69 | capy-factory | shared | 846 | n/a | n/a | every Capy session | untested | plan: none (grandfathered) | +| 70 | unfreeze | skill | 372 | 0 | 65 | every use | safety | reason: safety guard: the off switch for freeze's edit restriction; kept with the guard it releases | +| 71 | claude-code | skill | n/a | n/a | n/a | every use | untested | plan: none (grandfathered) | + +## Measure next + +Untested parts, largest load per run first. A fair test runs the claude_code preset, frontier models (Opus 5.5 and GPT-6.1 Sol, plus at most one named low-cost primary) and a masked no-gstack arm ([TESTING_INTERNALS.md](TESTING_INTERNALS.md#no-gstack-arm)). Safety rows are not on this list. + +| Order | Part | Load per run (tokens) | Plan | +|---|---|---|---| +| 1 | ship-review-panel | 22,830 | GEVA-2 registry row R56-1 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered) (grandfathered) | +| 2 | design-review | 20,159 | GEVA-2 registry row R56-9 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered) (grandfathered) | +| 3 | ship | 14,587 | GEVA-2 registry row R56-4 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered) (grandfathered) | +| 4 | plan-ceo-review | 14,394 | GEVA-2 registry row R56-5 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered) (grandfathered) | +| 5 | review | 13,990 | R2 (GEVA-2, 2026-11-02): gstack-evals wiki/plans/2026-11-02-geva-122-r2-amendment (#129, main eae6efc), told /review vs a no-gstack arm, Opus 5.5, preset on both arms (grandfathered) | +| 6 | devex-review | 13,911 | GEVA-2 registry row R56-11 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered) (grandfathered) | +| 7 | plan-design-review | 13,351 | GEVA-2 registry row R56-7 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered) (grandfathered) | +| 8 | retro | 12,257 | GEVA-2 registry row R56-12 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered) (grandfathered) | +| 9 | plan-devex-review | 11,104 | GEVA-2 registry row R56-6 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered) (grandfathered) | +| 10 | autoplan-phases | 10,709 | folded into GEVA-2 registry row R56-3 (autoplan); proposed, not yet preregistered (grandfathered) | +| 11 | office-hours | 10,424 | none (grandfathered) | +| 12 | autoplan | 10,288 | GEVA-2 registry row R56-3 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered) (grandfathered) | +| 13 | plan-eng-review | 9,837 | GEVA-2 registry row R56-2 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered) (grandfathered) | +| 14 | design-consultation | 9,557 | GEVA-2 registry row R56-10 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered) (grandfathered) | +| 15 | setup-gbrain | 9,099 | none (grandfathered) | +| 16 | codex | 8,991 | none (grandfathered) | +| 17 | sync-gbrain | 8,863 | none (grandfathered) | +| 18 | design-shotgun | 8,470 | none (grandfathered) | +| 19 | plan-tune | 8,377 | none (grandfathered) | +| 20 | spec | 8,305 | none (grandfathered) | +| 21 | browse | 8,125 | none (grandfathered) | +| 22 | design-html | 8,096 | none (grandfathered) | +| 23 | land-and-deploy | 7,652 | GEVA-2 registry row R56-8 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered) (grandfathered) | +| 24 | benchmark | 7,430 | none (grandfathered) | +| 25 | catalog | 7,291 | Run D (GEVA-2, 2026-11-02): gstack-evals wiki/plans/2026-11-01-geva-125-untold-discovery-prereg (#125, main 9820b14), untold gstack vs placebo vs no gstack, preset vs stub, Opus 5.5, GPT-6.1 Sol and particle-1; also measures /investigate's description (grandfathered) | +| 26 | canary | 7,034 | none (grandfathered) | +| 27 | qa | 6,951 | none (grandfathered) | +| 28 | scrape | 6,647 | none (grandfathered) | +| 29 | captain | 6,522 | none (grandfathered) | +| 30 | preamble | 5,743 | I1 ceremony cut (GEVA-2, 2026-11-02): gstack-evals wiki/plans/2026-11-01-geva-108-ceremony-cut-prereg (#122, main 409ff47) with its G3 amendment (#128), preset, Opus 5.5 and particle-1 (grandfathered) | +| 31 | gstack-upgrade | 5,726 | none (grandfathered) | +| 32 | context-restore | 5,715 | none (grandfathered) | +| 33 | document-generate | 5,466 | none (grandfathered) | +| 34 | ios-qa | 5,368 | none (grandfathered) | +| 35 | skillify | 5,317 | none (grandfathered) | +| 36 | capy-skill | 5,309 | none (grandfathered) | +| 37 | health | 5,287 | none (grandfathered) | +| 38 | setup-deploy | 5,216 | none (grandfathered) | +| 39 | qa-only | 5,205 | none (grandfathered) | +| 40 | eval-plan | 5,021 | none (grandfathered) | +| 41 | gstack | 4,951 | none (grandfathered) | +| 42 | pair-agent | 4,811 | none (grandfathered) | +| 43 | make-pdf | 4,730 | none (grandfathered) | +| 44 | context-save | 4,637 | none (grandfathered) | +| 45 | open-gstack-browser | 4,429 | none (grandfathered) | +| 46 | design-motion | 4,295 | none (grandfathered) | +| 47 | test-audit | 4,201 | none (grandfathered) | +| 48 | document-release | 4,049 | none (grandfathered) | +| 49 | diagram | 3,895 | none (grandfathered) | +| 50 | benchmark-models | 3,822 | none (grandfathered) | +| 51 | ubicloud | 3,593 | none (grandfathered) | +| 52 | modernize | 3,429 | none (grandfathered) | +| 53 | setup-browser-cookies | 3,184 | none (grandfathered) | +| 54 | deslop-shared-libs | 3,100 | none (grandfathered) | +| 55 | landing-report | 2,971 | none (grandfathered) | +| 56 | ship-docs-audit | 2,730 | folded into GEVA-2 registry row R56-4 (ship); proposed, not yet preregistered (grandfathered) | +| 57 | verify | 2,679 | none (grandfathered) | +| 58 | ios-design-review | 2,367 | none (grandfathered) | +| 59 | ios-sync | 2,240 | none (grandfathered) | +| 60 | ios-clean | 2,214 | none (grandfathered) | +| 61 | write-skill | 2,158 | none (grandfathered) | +| 62 | ios-fix | 2,096 | none (grandfathered) | +| 63 | learn | 1,989 | none (grandfathered) | +| 64 | capy-factory | 846 | none (grandfathered) | +| 65 | claude-code | n/a | none (grandfathered) | + +## Shared parts and preamble tiers + +| Tier | Skills | What loads | +|---|---|---| +| 1 | benchmark, benchmark-models, browse, diagram, gstack, make-pdf, open-gstack-browser, scrape, setup-browser-cookies, verify | a short preamble inline in SKILL.md | +| 2 | canary, captain, context-restore, context-save, design-html, design-motion, design-shotgun, document-generate, document-release, eval-plan, health, investigate, ios-clean, ios-design-review, ios-fix, ios-sync, landing-report, learn, modernize, pair-agent, plan-tune, retro, setup-deploy, setup-gbrain, skillify, sync-gbrain, test-audit, ubicloud, write-skill | the shared sections/preamble.md (row preamble), once per session | +| 3 | autoplan, claude-code, codex, design-consultation, devex-review, ios-qa, office-hours, plan-ceo-review, plan-design-review, plan-devex-review, plan-eng-review, spec | the shared sections/preamble.md (row preamble), once per session | +| 4 | design-review, land-and-deploy, qa, qa-only, review, ship | the shared sections/preamble.md (row preamble), once per session | +| none | careful, cso, deslop-shared-libs, freeze, gstack-upgrade, guard, unfreeze | no preamble | + +- **preamble**: shared preamble (sections/preamble.md, identical in every tier 2+ skill) (ship/sections/preamble.md); loads every tier 2+ skill run, inside that skill's load. +- **catalog**: skill catalog (every skill's frontmatter); loads every session. +- **capy-skill**: capy/skill/gstack/SKILL.md (capy/skill/gstack/SKILL.md); loads every Capy session. +- **capy-factory**: capy/skill/gstack/factory.md (capy/skill/gstack/factory.md); loads every Capy session. +- **ship-review-panel**: /ship review panel (review-army and adversarial sections) (ship/sections/review-army.md, ship/sections/adversarial.md); loads every /ship run, by depth. +- **ship-docs-audit**: /ship docs audit (Step 14.5) (ship/sections/documentation.md); loads every /ship run. +- **autoplan-phases**: /autoplan CEO, design, DX and eng phases (autoplan/sections/ceo-phase.md, autoplan/sections/design-phase.md, autoplan/sections/dx-phase.md, autoplan/sections/eng-phase.md); loads every /autoplan run, by scope. + +## Paid dispatches per run + +Each count is quoted from the file and line named; `bin/gstack-inventory --check` fails when a quoted line moves away. + +| Skill | What | Count | Source | +|---|---|---|---| +| ship | Step 9 review, pass 1, light depth, docs or tests only | 1 reader (the core checklist) | docs/review-policy.md:194 | +| ship | Step 9 review, pass 1, light depth, code | 3 readers (core, one batched specialist, native adversarial), plus the outside voice on a diff that publishes numbers | docs/review-policy.md:195 | +| ship | Step 9 review, pass 1, standard depth | the batched specialist, the Red Team at 200 runtime lines, adversarial and outside voice | docs/review-policy.md:196 | +| ship | Step 9 review, pass 1, full depth | the batched specialist on the whole diff and the Red Team always | docs/review-policy.md:197 | +| ship | targeted reader on money, gate, auth or secret code | +1 at every depth | docs/review-policy.md:319 | +| ship | audits (Steps 7, 8 and 14.5) | 3 | lib/ship-budget-calibration.json:3 | +| ship | measured: one /ship child (gstack-evals#31) | 10 review dispatches | lib/ship-budget-calibration.json:43 | +| ship | prior cost of one dispatch on the Capy meter | 7,430,000 tokens | lib/ship-budget-calibration.json:56 | +| autoplan | voices per phase (native and outside) | 2 | autoplan/DRIVER.md.tmpl:25 | +| autoplan | phases per round: CEO and Eng always, Design with UI scope, DX with developer-facing scope | 2 to 4, so 4 to 8 voice dispatches a round | autoplan/SKILL.md.tmpl:99 | +| autoplan | CEO spec review loop | up to 3 launches | autoplan/sections/ceo-phase.md.tmpl:50 | +| autoplan | review rounds per phase (new unattended runs) | up to 2 | lib/autoplan-budget.ts:23 | +| autoplan | reserved per voice on Capy or under a spend cap | $5 | lib/autoplan-budget.ts:25 | + +## Earlier results that do not count yet + +- `stub`: ran on the Agent SDK stub prompt, not the claude_code preset. +- `small-tier`: Haiku, Sonnet or another non-frontier model only. +- `unmasked`: no mask recorded: the no-gstack arm predates the masking rule and was not re-checked. +- `ceiling`: control at the ceiling, so no difference could show. +- `no-true-no-gstack-arm`: the none arm still had gstack installed. +- `no-exposure`: the model never used the part. +- `not-claude-code`: ran outside Claude Code (Messages API, Codex or Capy). +- `variant-only`: variant against variant, no no-gstack arm. + +| Part | Result | Why it does not count | Note | +|---|---|---|---| +| investigate | gstack-evals #109 stacks round 1 TB2 (GEVA-89, GEVA-93), wiki/board/2026-11-01-geva-93-stacks-round-1-tb2-results | stub, no-exposure, unmasked | Opus +3 points [-3, +9] at 1.9x cost; debug report in 0 of 111 trials; particle-1 -5 [-14, +1] | +| investigate | GSTA-12 Lane B quick look 2, wiki/board/2026-10-29-gsta-12-laneb-quick-2-results | stub, unmasked | Opus gstack minus none -7 [-13, -2] at 1.7x cost; did not replicate | +| investigate | GSTA-30 Lane B quick look 3 (#89), wiki/board/2026-10-30-gsta-30-laneb-quick-3-results | stub, small-tier | gstack arms on Sonnet, particle-1, DeepSeek and Haiku; Opus ran the none arm only | +| investigate | HARN-14 replication (harness-evals #181, unmerged) | stub, small-tier | Haiku 5.5: gstack minus none +3.5 [-12.3, +20.0] | +| learn | GSTA-13, GEVA-54 and GEVA-65 Lane A runs (wiki/board/2026-10-28-gsta-13-quick-look-3-results and the GEVA-54 and GEVA-65 posts) | stub, no-true-no-gstack-arm, ceiling | Opus contract tasks 27/30 vs 27/30 | +| learn | GEVA-65 Lane A rerun row 5 (GPT-6.1 Sol in Codex) | not-claude-code, no-true-no-gstack-arm | Sol failed the no-code-change safety task in 5 of 9 skill trials vs 0 of 3 without; small n, a lead to check, not a hurts verdict | +| office-hours | GSTA-13 quick look 3 Lane A, wiki/board/2026-10-28-gsta-13-quick-look-3-results | stub, no-true-no-gstack-arm | contract tasks on Opus: gstack 74% vs none 28%; the tasks grade the skill's own contract, a manipulation check, not lift | +| office-hours | GEVA-65 Lane A rerun, wiki/board/2026-10-31-geva-65-lanea-shakedown-rerun-results | stub, no-true-no-gstack-arm | Opus before 0.67, after 0.76, none 0.36 | +| plan-ceo-review | CEO panel v2 admission (GEVA-142), wiki/board/2026-11-02-geva-142-ceo-panel-v2-admission-results (open PR #131) | ceiling, unmasked | preset, Opus 5.5 only: must-fix recall 1.00 in all three arms; plain invocation ran the procedure in 0 of 16 | +| plan-ceo-review | Planning R1a pilot (GEVA-102), wiki/board/2026-11-01-geva-102-planning-r1a-pilot-stop (#114) | stub, ceiling, unmasked | none-opus 8/8; gstack-opus read the sections file in 0 of 4 | +| review | gstack-evals #108 stacks round 1 review (GEVA-89), wiki/board/2026-11-01-geva-89-stacks-round-1-review-results | stub, no-exposure, unmasked | Opus 5.5 blocking recall 9.5% vs 7.1%, +2.4 points [-6.4, +11.1]; specialists and adversarial pass ran in 0 of 66 sessions | +| review | gstack-evals #120 R1 blind re-judge (GEVA-113), wiki/board/2026-11-01-geva-113-r1-blind-rejudge-results | stub, unmasked | gstack-opus minus none-opus +3.5 points [-9.6, +16.7] | +| review | GSTA-11 quick look 1 review replay, wiki/board/2026-10-28-gsta-11-review-replay-results | not-claude-code, unmasked | /review's prompts through the Messages API; Opus recall 44% vs 39% plain, 12 bugs | +| preamble | GEVA-100 exposure counts, org-gstack-gbrain/research/why-null-stacks-r1.md (GEVA-100) | stub, no-exposure | ceremony sections ran in 0 of 66 Opus review sessions and 6 of 111 Opus TB2 trials; no effect estimate | +| catalog | GSTA-12 Lane B quick look 1, wiki/board/2026-10-29-gsta-12-laneb-quick-results | stub, no-exposure, unmasked | untold skill opened in 0 of 180 gstack-arm trials; Opus gstack minus none +5 [-3, +17] | +| ship-review-panel | gstack-evals #120 R1 blind re-judge (GEVA-113), wiki/board/2026-11-01-geva-113-r1-blind-rejudge-results | stub, not-claude-code, unmasked | arm C minus none +56.9 points [40.5, 72.2]; the none arm ran on the stub, arm C in Capy at another gstack version, on a cohort mined from gstack's own catches | +| ship-review-panel | GSTA-48 review no-loss arm D (#77), wiki/board/2026-10-30-gsta-48-review-noloss-arm-how-to | variant-only, not-claude-code | arm D vs pooled arm C, no no-gstack arm | + +## Key list and aliases + +71 keys: 64 skills (the folders `discoverTemplates()` finds, the root skill as `gstack`) and 7 shared parts. + +- Skills: autoplan, benchmark, benchmark-models, browse, canary, captain, careful, claude-code, codex, context-restore, context-save, cso, design-consultation, design-html, design-motion, design-review, design-shotgun, deslop-shared-libs, devex-review, diagram, document-generate, document-release, eval-plan, freeze, gstack, gstack-upgrade, guard, health, investigate, ios-clean, ios-design-review, ios-fix, ios-qa, ios-sync, land-and-deploy, landing-report, learn, make-pdf, modernize, office-hours, open-gstack-browser, pair-agent, plan-ceo-review, plan-design-review, plan-devex-review, plan-eng-review, plan-tune, qa, qa-only, retro, review, scrape, setup-browser-cookies, setup-deploy, setup-gbrain, ship, skillify, spec, sync-gbrain, test-audit, ubicloud, unfreeze, verify, write-skill. +- Shared: preamble, catalog, capy-skill, capy-factory, ship-review-panel, ship-docs-audit, autoplan-phases. +- Installed names: `gstack-` is key `` (`gstack-ship` is `ship`); `gstack-upgrade` keeps its name; the root skill installs as `gstack`. +- Other names (the ledger's `aliases`): gstack (root skill) → gstack; upgrade → gstack-upgrade; preamble (ceremony) → preamble; always-on skill catalog (descriptions) → catalog; capy/skill/gstack/SKILL.md (Capy context) → capy-skill; capy/skill/gstack/factory.md (Capy context) → capy-factory; ship: review panel (specialist, adversarial, targeted, outside voice, red team) → ship-review-panel; ship: docs audit (Step 14.5) → ship-docs-audit; autoplan: CEO, design, DX and eng phases, outside voice → autoplan-phases. + +## Not in this inventory + +- Out of scope: openclaw/skills/* and browser-skills/* (not gstack skills an agent loads from this checkout), and connect-chrome (a symlink to open-gstack-browser, counted once). +- Evidence-table row "review: Review Army and adversarial pass": a section inside /review, not its own key; R2 measures it as part of the review row's plan. +- Evidence-table row "ship: QA probes (Step 9.2.1)": a step inside /ship with no separate size; folded into the ship row (GEVA-2 registry row R56-4). +- Evidence-table row "gstack as a whole (installed, any skill)": not a single text part; GEVA-2's real-world outcome lane measures it (gstack-evals plan #111), not a measurement plan for any one part. diff --git a/docs/inventory/evidence.json b/docs/inventory/evidence.json new file mode 100644 index 000000000..6600f7b7e --- /dev/null +++ b/docs/inventory/evidence.json @@ -0,0 +1,661 @@ +{ + "schema_version": 1, + "note": "R56 evidence ledger (ETHOS.md: 9. Earn Its Place). One row per inventory key (docs/inventory.md lists them); bin/gstack-inventory --check validates it and test/inventory.test.ts runs that check. Statuses and their required fields: helps, no-effect, hurts need sources, models (frontier per R54), prompt: preset, scope and masked: true, and no-effect and hurts also r48 {verdict, ref}; untested needs plan (a reference, or none only when grandfathered), and a new part also reason, owner and measure_by; safety needs reason. prior lists earlier results that do not count yet, with why. Seeded 2026-11-02 from GEVA-2's table (org-gstack-gbrain/research/gstack-part-evidence.json, gstack dac71371a); every row present at that base is grandfathered.", + "seed_source": "org-gstack-gbrain/research/gstack-part-evidence.json (GEVA-156 for GST-915, 2026-11-02, gstack-evals main 409ff47)", + "parts": { + "autoplan": { + "status": "untested", + "grandfathered": true, + "plan": "GEVA-2 registry row R56-3 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered)", + "seed": "GEVA-2, 2026-11-02: autoplan: untested (never measured)" + }, + "benchmark": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: benchmark: untested (never measured)" + }, + "benchmark-models": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: benchmark-models: untested (never measured)" + }, + "browse": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: browse: untested (never measured)" + }, + "canary": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: canary: untested (never measured)" + }, + "captain": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: captain: untested (never measured)" + }, + "careful": { + "status": "safety", + "grandfathered": true, + "reason": "safety guard: warns before destructive commands (rm -rf, DROP TABLE, force-push); kept on that reason, an average score cannot see the rare bad run it stops", + "seed": "GEVA-2, 2026-11-02: careful: exempt (safety guard)" + }, + "claude-code": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: claude-code: untested (never measured)" + }, + "codex": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "not in GEVA-2's table (2026-11-02); never measured" + }, + "context-restore": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: context-restore: untested (never measured)" + }, + "context-save": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: context-save: untested (never measured)" + }, + "cso": { + "status": "safety", + "grandfathered": true, + "reason": "security: supported security findings with explicit coverage; kept on its security reason (GEVA-2 marks it exempt; GStack Minder may reclassify it)", + "seed": "GEVA-2, 2026-11-02: cso: exempt (security audit skill, treated as exempt; GStack Minder may reclassify)" + }, + "design-consultation": { + "status": "untested", + "grandfathered": true, + "plan": "GEVA-2 registry row R56-10 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered)", + "seed": "GEVA-2, 2026-11-02: design-consultation: untested (never measured)" + }, + "design-html": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: design-html: untested (never measured)" + }, + "design-motion": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: design-motion: untested (never measured)" + }, + "design-review": { + "status": "untested", + "grandfathered": true, + "plan": "GEVA-2 registry row R56-9 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered)", + "seed": "GEVA-2, 2026-11-02: design-review: untested (never measured)" + }, + "design-shotgun": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: design-shotgun: untested (never measured)" + }, + "deslop-shared-libs": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: deslop-shared-libs: untested (never measured)" + }, + "devex-review": { + "status": "untested", + "grandfathered": true, + "plan": "GEVA-2 registry row R56-11 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered)", + "seed": "GEVA-2, 2026-11-02: devex-review: untested (never measured)" + }, + "diagram": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: diagram: untested (never measured)" + }, + "document-generate": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: document-generate: untested (never measured)" + }, + "document-release": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: document-release: untested (never measured)" + }, + "eval-plan": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: eval-plan: untested (never measured)" + }, + "freeze": { + "status": "safety", + "grandfathered": true, + "reason": "safety guard: hard-blocks edits outside one directory; kept on that reason", + "seed": "GEVA-2, 2026-11-02: freeze: exempt (safety guard)" + }, + "gstack": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: gstack (root skill): untested (never measured)", + "aliases": [ + "gstack (root skill)" + ] + }, + "gstack-upgrade": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: upgrade: untested (never measured)", + "aliases": [ + "upgrade" + ] + }, + "guard": { + "status": "safety", + "grandfathered": true, + "reason": "safety guard: careful and freeze together; kept on that reason", + "seed": "GEVA-2, 2026-11-02: guard: exempt (safety guard)" + }, + "health": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: health: untested (never measured)" + }, + "investigate": { + "status": "no-effect", + "grandfathered": true, + "sources": [ + "GST-814 (HARN-1, 2026-11-02)", + "org-gstack-gbrain handoffs/HARN-14/hard-follow-2026-11/decision-rule.md" + ], + "models": [ + "claude-opus-5-5" + ], + "prompt": "preset", + "scope": "skill step procedure follow rate: 0 of 46 gstack sessions ran any step (0 of 30 on the claude_code preset); body loaded every time; the outcome check voided at ceiling (no-gstack fixed 8 of 8)", + "masked": true, + "r48": { + "verdict": "CUT-CANDIDATE", + "ref": "GST-938 (GSTA-251 plans hooks-or-drop)" + }, + "plan": "R3 (GEVA-2, 2026-11-02): gstack-evals wiki/plans/2026-11-01-geva-101-r48-follow-ups-prereg (#112, main f0b81c0) and its amendment (open PR #133), told /investigate vs no gstack on SWE-bench Verified, preset, Opus 5.5 then GPT-6.1 Sol", + "prior": [ + { + "ref": "gstack-evals #109 stacks round 1 TB2 (GEVA-89, GEVA-93), wiki/board/2026-11-01-geva-93-stacks-round-1-tb2-results", + "why": [ + "stub", + "no-exposure", + "unmasked" + ], + "note": "Opus +3 points [-3, +9] at 1.9x cost; debug report in 0 of 111 trials; particle-1 -5 [-14, +1]" + }, + { + "ref": "GSTA-12 Lane B quick look 2, wiki/board/2026-10-29-gsta-12-laneb-quick-2-results", + "why": [ + "stub", + "unmasked" + ], + "note": "Opus gstack minus none -7 [-13, -2] at 1.7x cost; did not replicate" + }, + { + "ref": "GSTA-30 Lane B quick look 3 (#89), wiki/board/2026-10-30-gsta-30-laneb-quick-3-results", + "why": [ + "stub", + "small-tier" + ], + "note": "gstack arms on Sonnet, particle-1, DeepSeek and Haiku; Opus ran the none arm only" + }, + { + "ref": "HARN-14 replication (harness-evals #181, unmerged)", + "why": [ + "stub", + "small-tier" + ], + "note": "Haiku 5.5: gstack minus none +3.5 [-12.3, +20.0]" + } + ], + "seed": "GEVA-2, 2026-11-02: investigate: measurement under way (R3)", + "note": "Only Opus 5.5 (claude-opus-5-5) was measured; GPT-6.1 Sol was not, so this verdict covers one of the two frontier models ETHOS.md 9 names." + }, + "ios-clean": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: ios-clean: untested (never measured)" + }, + "ios-design-review": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: ios-design-review: untested (never measured)" + }, + "ios-fix": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: ios-fix: untested (never measured)" + }, + "ios-qa": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: ios-qa: untested (never measured)" + }, + "ios-sync": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: ios-sync: untested (never measured)" + }, + "land-and-deploy": { + "status": "untested", + "grandfathered": true, + "plan": "GEVA-2 registry row R56-8 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered)", + "seed": "GEVA-2, 2026-11-02: land-and-deploy: untested (never measured)" + }, + "landing-report": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: landing-report: untested (never measured)" + }, + "learn": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "prior": [ + { + "ref": "GSTA-13, GEVA-54 and GEVA-65 Lane A runs (wiki/board/2026-10-28-gsta-13-quick-look-3-results and the GEVA-54 and GEVA-65 posts)", + "why": [ + "stub", + "no-true-no-gstack-arm", + "ceiling" + ], + "note": "Opus contract tasks 27/30 vs 27/30" + }, + { + "ref": "GEVA-65 Lane A rerun row 5 (GPT-6.1 Sol in Codex)", + "why": [ + "not-claude-code", + "no-true-no-gstack-arm" + ], + "note": "Sol failed the no-code-change safety task in 5 of 9 skill trials vs 0 of 3 without; small n, a lead to check, not a hurts verdict" + } + ], + "seed": "GEVA-2, 2026-11-02: learn: untested (unfair test: SDK stub; no true no-gstack arm; Opus at ceiling; Sol ran in Codex, not Claude Code)" + }, + "make-pdf": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: make-pdf: untested (never measured)" + }, + "modernize": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: modernize: untested (never measured)" + }, + "office-hours": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "prior": [ + { + "ref": "GSTA-13 quick look 3 Lane A, wiki/board/2026-10-28-gsta-13-quick-look-3-results", + "why": [ + "stub", + "no-true-no-gstack-arm" + ], + "note": "contract tasks on Opus: gstack 74% vs none 28%; the tasks grade the skill's own contract, a manipulation check, not lift" + }, + { + "ref": "GEVA-65 Lane A rerun, wiki/board/2026-10-31-geva-65-lanea-shakedown-rerun-results", + "why": [ + "stub", + "no-true-no-gstack-arm" + ], + "note": "Opus before 0.67, after 0.76, none 0.36" + } + ], + "seed": "GEVA-2, 2026-11-02: office-hours: untested (unfair test: SDK stub; no true no-gstack arm; tasks grade the skill's own contract, a manipulation check, not lift)" + }, + "open-gstack-browser": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: open-gstack-browser: untested (never measured)" + }, + "pair-agent": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: pair-agent: untested (never measured)" + }, + "plan-ceo-review": { + "status": "untested", + "grandfathered": true, + "plan": "GEVA-2 registry row R56-5 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered)", + "prior": [ + { + "ref": "CEO panel v2 admission (GEVA-142), wiki/board/2026-11-02-geva-142-ceo-panel-v2-admission-results (open PR #131)", + "why": [ + "ceiling", + "unmasked" + ], + "note": "preset, Opus 5.5 only: must-fix recall 1.00 in all three arms; plain invocation ran the procedure in 0 of 16" + }, + { + "ref": "Planning R1a pilot (GEVA-102), wiki/board/2026-11-01-geva-102-planning-r1a-pilot-stop (#114)", + "why": [ + "stub", + "ceiling", + "unmasked" + ], + "note": "none-opus 8/8; gstack-opus read the sections file in 0 of 4" + } + ], + "r48": { + "verdict": "FIX-METHOD", + "ref": "CEO panel v2 admission (GEVA-142, open PR #131): FIX-METHOD on the panel, a harder panel is planned" + }, + "seed": "GEVA-2, 2026-11-02: plan-ceo-review: untested (unfair test: ceiling, control 12 of 12 so no MDD; plain invocation ran the procedure in 0 of 16)" + }, + "plan-design-review": { + "status": "untested", + "grandfathered": true, + "plan": "GEVA-2 registry row R56-7 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered)", + "seed": "GEVA-2, 2026-11-02: plan-design-review: untested (never measured)" + }, + "plan-devex-review": { + "status": "untested", + "grandfathered": true, + "plan": "GEVA-2 registry row R56-6 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered)", + "seed": "GEVA-2, 2026-11-02: plan-devex-review: untested (never measured)" + }, + "plan-eng-review": { + "status": "untested", + "grandfathered": true, + "plan": "GEVA-2 registry row R56-2 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered)", + "seed": "GEVA-2, 2026-11-02: plan-eng-review: untested (never measured)" + }, + "plan-tune": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: plan-tune: untested (never measured)" + }, + "qa": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: qa: untested (never measured)" + }, + "qa-only": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: qa-only: untested (never measured)" + }, + "retro": { + "status": "untested", + "grandfathered": true, + "plan": "GEVA-2 registry row R56-12 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered)", + "seed": "GEVA-2, 2026-11-02: retro: untested (never measured)" + }, + "review": { + "status": "untested", + "grandfathered": true, + "plan": "R2 (GEVA-2, 2026-11-02): gstack-evals wiki/plans/2026-11-02-geva-122-r2-amendment (#129, main eae6efc), told /review vs a no-gstack arm, Opus 5.5, preset on both arms", + "prior": [ + { + "ref": "gstack-evals #108 stacks round 1 review (GEVA-89), wiki/board/2026-11-01-geva-89-stacks-round-1-review-results", + "why": [ + "stub", + "no-exposure", + "unmasked" + ], + "note": "Opus 5.5 blocking recall 9.5% vs 7.1%, +2.4 points [-6.4, +11.1]; specialists and adversarial pass ran in 0 of 66 sessions" + }, + { + "ref": "gstack-evals #120 R1 blind re-judge (GEVA-113), wiki/board/2026-11-01-geva-113-r1-blind-rejudge-results", + "why": [ + "stub", + "unmasked" + ], + "note": "gstack-opus minus none-opus +3.5 points [-9.6, +16.7]" + }, + { + "ref": "GSTA-11 quick look 1 review replay, wiki/board/2026-10-28-gsta-11-review-replay-results", + "why": [ + "not-claude-code", + "unmasked" + ], + "note": "/review's prompts through the Messages API; Opus recall 44% vs 39% plain, 12 bugs" + } + ], + "r48": { + "verdict": "FIX-METHOD", + "ref": "org-gstack-gbrain/research/why-null-stacks-r1.md (GEVA-100); R2 is the fix" + }, + "seed": "GEVA-2, 2026-11-02: review: measurement under way (R2)" + }, + "scrape": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: scrape: untested (never measured)" + }, + "setup-browser-cookies": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: setup-browser-cookies: untested (never measured)" + }, + "setup-deploy": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: setup-deploy: untested (never measured)" + }, + "setup-gbrain": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: setup-gbrain: untested (never measured)" + }, + "ship": { + "status": "untested", + "grandfathered": true, + "plan": "GEVA-2 registry row R56-4 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered)", + "seed": "GEVA-2, 2026-11-02: ship: untested (never measured)" + }, + "skillify": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: skillify: untested (never measured)" + }, + "spec": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: spec: untested (never measured)" + }, + "sync-gbrain": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: sync-gbrain: untested (never measured)" + }, + "test-audit": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: test-audit: untested (never measured)" + }, + "ubicloud": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: ubicloud: untested (never measured)" + }, + "unfreeze": { + "status": "safety", + "grandfathered": true, + "reason": "safety guard: the off switch for freeze's edit restriction; kept with the guard it releases", + "seed": "GEVA-2, 2026-11-02: unfreeze: exempt (safety guard (undoes freeze))" + }, + "verify": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "not in GEVA-2's table (2026-11-02); never measured" + }, + "write-skill": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: write-skill: untested (never measured)" + }, + "preamble": { + "status": "untested", + "grandfathered": true, + "plan": "I1 ceremony cut (GEVA-2, 2026-11-02): gstack-evals wiki/plans/2026-11-01-geva-108-ceremony-cut-prereg (#122, main 409ff47) with its G3 amendment (#128), preset, Opus 5.5 and particle-1", + "prior": [ + { + "ref": "GEVA-100 exposure counts, org-gstack-gbrain/research/why-null-stacks-r1.md (GEVA-100)", + "why": [ + "stub", + "no-exposure" + ], + "note": "ceremony sections ran in 0 of 66 Opus review sessions and 6 of 111 Opus TB2 trials; no effect estimate" + } + ], + "r48": { + "verdict": "CUT-CANDIDATE", + "ref": "org-gstack-gbrain/research/why-null-stacks-r1.md (GEVA-100); taken by Garry as the I1 ceremony-cut eval" + }, + "seed": "GEVA-2, 2026-11-02: preamble (ceremony): measurement under way (I1, ceremony-cut eval)", + "aliases": [ + "preamble (ceremony)" + ] + }, + "catalog": { + "status": "untested", + "grandfathered": true, + "plan": "Run D (GEVA-2, 2026-11-02): gstack-evals wiki/plans/2026-11-01-geva-125-untold-discovery-prereg (#125, main 9820b14), untold gstack vs placebo vs no gstack, preset vs stub, Opus 5.5, GPT-6.1 Sol and particle-1; also measures /investigate's description", + "prior": [ + { + "ref": "GSTA-12 Lane B quick look 1, wiki/board/2026-10-29-gsta-12-laneb-quick-results", + "why": [ + "stub", + "no-exposure", + "unmasked" + ], + "note": "untold skill opened in 0 of 180 gstack-arm trials; Opus gstack minus none +5 [-3, +17]" + } + ], + "seed": "GEVA-2, 2026-11-02: always-on skill catalog (descriptions): measurement under way (Run D)", + "aliases": [ + "always-on skill catalog (descriptions)" + ] + }, + "capy-skill": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: capy/skill/gstack/SKILL.md (Capy context): untested (never measured)", + "aliases": [ + "capy/skill/gstack/SKILL.md (Capy context)" + ], + "note": "loads only in Capy sessions, which the claude_code preset fair test cannot reach yet; needs a Captain call on how to test it" + }, + "capy-factory": { + "status": "untested", + "grandfathered": true, + "plan": "none", + "seed": "GEVA-2, 2026-11-02: capy/skill/gstack/factory.md (Capy context): untested (never measured)", + "aliases": [ + "capy/skill/gstack/factory.md (Capy context)" + ], + "note": "loads only in Capy sessions, which the claude_code preset fair test cannot reach yet; needs a Captain call on how to test it" + }, + "ship-review-panel": { + "status": "untested", + "grandfathered": true, + "plan": "GEVA-2 registry row R56-1 (org-gstack-gbrain/research/gstack-part-evidence.json registry_rows; proposed, not yet preregistered)", + "prior": [ + { + "ref": "gstack-evals #120 R1 blind re-judge (GEVA-113), wiki/board/2026-11-01-geva-113-r1-blind-rejudge-results", + "why": [ + "stub", + "not-claude-code", + "unmasked" + ], + "note": "arm C minus none +56.9 points [40.5, 72.2]; the none arm ran on the stub, arm C in Capy at another gstack version, on a cohort mined from gstack's own catches" + }, + { + "ref": "GSTA-48 review no-loss arm D (#77), wiki/board/2026-10-30-gsta-48-review-noloss-arm-how-to", + "why": [ + "variant-only", + "not-claude-code" + ], + "note": "arm D vs pooled arm C, no no-gstack arm" + } + ], + "seed": "GEVA-2, 2026-11-02: ship: review panel (specialist, adversarial, targeted, outside voice, red team): untested (unfair test: none arm on the SDK stub; arms from different harnesses and gstack versions; cohort mined from gstack's own catches)", + "aliases": [ + "ship: review panel (specialist, adversarial, targeted, outside voice, red team)" + ] + }, + "ship-docs-audit": { + "status": "untested", + "grandfathered": true, + "plan": "folded into GEVA-2 registry row R56-4 (ship); proposed, not yet preregistered", + "seed": "GEVA-2, 2026-11-02: ship: docs audit (Step 14.5): untested (never measured)", + "aliases": [ + "ship: docs audit (Step 14.5)" + ] + }, + "autoplan-phases": { + "status": "untested", + "grandfathered": true, + "plan": "folded into GEVA-2 registry row R56-3 (autoplan); proposed, not yet preregistered", + "seed": "GEVA-2, 2026-11-02: autoplan: CEO, design, DX and eng phases, outside voice: untested (never measured)", + "aliases": [ + "autoplan: CEO, design, DX and eng phases, outside voice" + ] + } + }, + "unmapped": [ + { + "row": "review: Review Army and adversarial pass", + "why": "a section inside /review, not its own key; R2 measures it as part of the review row's plan" + }, + { + "row": "ship: QA probes (Step 9.2.1)", + "why": "a step inside /ship with no separate size; folded into the ship row (GEVA-2 registry row R56-4)" + }, + { + "row": "gstack as a whole (installed, any skill)", + "why": "not a single text part; GEVA-2's real-world outcome lane measures it (gstack-evals plan #111), not a measurement plan for any one part" + } + ] +} diff --git a/docs/pregate.md b/docs/pregate.md index 66771f548..c8fb847a5 100644 --- a/docs/pregate.md +++ b/docs/pregate.md @@ -86,7 +86,7 @@ the byte-pinned `/ship` renders in `test/fixtures/golden/`). | `sealed` | sealed or eval output that must never reach a public destination; read by the pre-push hook and `gstack-pregate sealed` (see [sealed output](#sealed)) | | `price_file` | repo-relative path of the paid gate's call-plan data file (`GSTACK_EVAL_CALLS v1`); `gstack-eval-plan price-gate` prices from it and never executes anything ([docs/eval-calls.md](eval-calls.md)) | | `stall_timeout_s` | seconds a test command may print nothing while using under a tenth of one CPU before its process group is killed and the check is `incomplete` (`PREGATE_STALLED`); default 300, `0` disables; `--stall-timeout` overrides; the `--remote` gate is exempt (see [supervision](#supervision)) | -| `timeout_s` | seconds each runner process may take (the unit-file group, each serial file, each guard without its own `timeout_s`, and the remote gate's deadline) when `--timeout` is not passed; 1 to 3600, default 600. `gstack-ship-preflight` and the `finish` fallback run pregate without `--timeout`, so this is how a repo with long lanes sets theirs | +| `timeout_s` | seconds each runner process may take (the unit-file group, each serial file, each guard without its own `timeout_s`, and the remote gate's deadline) when `--timeout` is not passed; 1 to 3600, default 600. `gstack-ship-preflight` forwards its own `--timeout`; without it, and in the `finish` fallback, this is how a repo with long lanes sets theirs | | `remote_gate` | the repo's remote test gate command; under 8 cores the local test tier becomes a `requires-remote` obligation naming it, and `--remote` runs it (see [remote gate](#remote-gate)) | | `run_profiles` | name → `{ workflow, events, jobs, artifact, max_age_hours?, os?, skippable? }`: the GitHub Actions runs `clear --run` verifies as evidence on the identical tree; a profile named after a lane (`free-tests.yml/macos-named-regressions`) clears that lane (see [run evidence](#run-evidence)). `--from-run` and the main-push reuse job verify runs against them too ([from-run](#from-run), [CI reuse](#ci-reuse)) | | `remote_gate_runs` | `{ profiles, inventory, skipped? }`: the run profiles that together clear `pregate.json/remote_gate`, the command that prints the remote gate's test files, one per line, which their runs must cover, and an optional command that prints which selected files the gate itself never runs (see [run evidence](#run-evidence)) | @@ -173,7 +173,7 @@ The list lives in its own file, not in `pregate.json`, because older pregate rea ## `gstack-pregate` ``` -gstack-pregate [--stage preflight|tests|all] [--only ] [--allow-repo-commands] [--policy-from base|head] +gstack-pregate [--stage preflight|tests|guards|all] [--only ] [--allow-repo-commands] [--policy-from base|head] [--timeout ] [--stall-timeout ] [--remote --spend-cap ] [--run ] [--from-run ] [--no-ci-carry] [--explain] [--json] gstack-pregate verify | clear --remote --run-url | lanes | validate | init ``` @@ -765,10 +765,12 @@ once: such as `"per_file": "bun test --timeout=30000 {files}"`, the same form as the tests stage's `runner`). That run is recorded under `