Files
gstack/ship/sections/tests.md.tmpl
T
garrytanandcapy-ai[bot] 4c2d3fe011 docs(ship): Step 5 pre-gate text for stalls and the small-machine remote gate (Lane C-c)
The pre-gate paragraph now says `incomplete` blocks (a stall is incomplete)
and that `requires-remote` covers the remote gate under 8 cores, cleared by
`gstack-pregate clear` or `--remote --spend-cap <usd>` only under a cap the
user gave. Kept within the ship parity size cap (union 308,771 of 308,776
bytes; base 308,767), so the `--explain` hint moved to docs/pregate.md. The
handoff's escalation line already names any `requires-remote` lane; unchanged.

Files (G2): ship/sections/tests.md.tmpl, ship/sections/tests.md (generated).

Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
2026-10-15 00:23:34 +00:00

145 lines
7.1 KiB
Cheetah

## Step 4: Test Framework Bootstrap
{{TEST_BOOTSTRAP}}
---
## Step 5: Run tests (on merged code)
**Pre-gate first** — free, before any lane, eval or remote gate:
```bash
~/.claude/skills/gstack/bin/gstack-pregate --allow-repo-commands
```
Stage 1 (regen, secrets, strays, literals) is the two-minute preflight; stage 2
runs the selected lanes under pipefail. `fail` or `incomplete` blocks: fix,
rerun. `requires-remote` (other platforms; the remote gate under 8 cores) needs
`gstack-pregate clear` or, under a user cap, `--remote --spend-cap <usd>`. Rerun
after the final stamp; Step 18 refuses a stale one.
Use the project's test commands discovered in Step 4 or documented in CLAUDE.md/AGENTS.md. Run every applicable suite; do not assume Rails or Vitest. The commands below are examples only for repositories that actually provide them. Use the same lane labels and exact commands again in Step 16.
**If no applicable test suite exists:** Name the untested scope. AskUserQuestion:
A) Add tests (recommended), B) Ship with this named testing
gap, or C) Stop. Reuse an actual prior B answer only for the same scope and
content; declining bootstrap alone is not that approval. B continues with the
gap recorded, not passing tests. Independent build, eval, review and QA gates
still apply. A declared but unavailable suite is a blocker, not an absent suite.
A runs Step 4 with this new bootstrap choice, then returns here to run the tests.
C stops this attempt.
**For Rails projects using `bin/test-lane`, do NOT run `RAILS_ENV=test bin/rails db:migrate`** — `bin/test-lane` already calls
`db:test:prepare` internally, which loads the schema into the correct lane database.
Running bare test migrations without INSTANCE hits an orphan DB and corrupts structure.sql.
Run independent test suites in parallel, each wrapped in the evidence ledger. The
wrapper is transparent (streams output live, exit code passes through) and
records `{command, exit, working-tree fingerprint, log path}` to
`$GSTACK_STATE_ROOT/projects/<slug>/<branch>-evidence.jsonl` — Step 16 cites this
record instead of re-running when the content hasn't changed:
```bash
set -o pipefail; pids=()
~/.claude/skills/gstack/bin/gstack-evidence run --label tests -- 'bin/test-lane 2>&1' & pids+=($!)
~/.claude/skills/gstack/bin/gstack-evidence run --label vitest -- 'npm run test 2>&1' & pids+=($!)
fail=0; for p in "${pids[@]}"; do wait "$p" || fail=1; done; echo "lanes failed=$fail"
```
After all suites complete, check the `gstack-evidence: recorded label=... exit=...
log=...` summary lines — each carries the lane's exit code and a per-run log
file (no shared /tmp collisions between concurrent ships). Read the log files
for failure detail.
**A lane passes on its runner summary line, not on exit 0 alone.** Read each
lane's `tests_ran` from its receipt (or from the runner's own summary in the log)
and keep the three states apart; never merge them:
1. `tests_ran: N` (N > 0) with exit 0 — the lane passed; record N for the PR body.
2. `tests_ran: 0` — **ZERO-RUN**: bun `0 pass 0 fail`, jest "No tests found", pytest
"no tests ran", rspec "0 examples", `go test` with only `[no test files]`, or an
empty selection after a filter. Exit 0 here is not a pass, so triage a ZERO-RUN
lane below as a failed lane: a bad selector, a missing file or a skipped `describe`
is an in-branch failure until proven otherwise.
3. `tests_ran: unknown` — the runner's summary was not recognised. Keep today's
exit-0 pass and record `count unavailable` for that lane in the PR body.
**If any test fails:** Do NOT immediately stop. When a free-suite shard failed,
first Read `~/.claude/skills/gstack/ship/sections/measure.md` and rerun that
shard's file list as it says (diagnostic, not a verdict). Then apply the Test
Failure Ownership Triage:
{{TEST_FAILURE_TRIAGE}}
**After triage:** If any in-branch failures remain unfixed, **STOP**. Do not proceed. If all failures were pre-existing and handled (fixed, TODOed, assigned, or skipped), continue to Step 6.
**If all pass:** Report each lane's pass count (or `count unavailable`) in one line and continue to Step 6.
---
## Step 6: Eval Suites (conditional)
Evals are mandatory when prompt-related files change. Select from the full diff,
including uncommitted changes, before deciding whether to skip.
**1. Select affected suites using the project's contract.**
**Project-native path:** Read CLAUDE.md/AGENTS.md, package scripts and the eval
dependency map. Include changed prompts, skill templates, judges and harness
code. Use the documented selector and pre-merge command. If it reports no
affected suites, record that result and continue to Step 7. If prompt-related
files changed but selection or the command is unknown, report the validation
gap and ask before shipping. A missing Rails-pattern match is not a skip signal
for another stack.
**Rails example only — when this repository provides `bin/test-lane` and
`test/evals/*_eval_runner.rb`:**
- Match the diff against the project's documented prompt paths, such as
`app/services/*_prompt_builder.rb`, generation/writer/designer services,
evaluator/scorer/classifier/analyzer services, voice/writing/prompt/token
concerns, chat tools, `config/system_prompts/*.txt` and `test/evals/**/*`.
- Match changed files to each runner's `PROMPT_SOURCE_FILES`; follow shared
judge/support/fixture imports to all affected suites. A runner such as
`post_generation_eval_runner.rb` maps to `post_generation_eval_test.rb`.
- Use the project's full pre-merge tier (`EVAL_JUDGE_TIER=full` for this runner).
Do not substitute a cheaper development tier. If selection remains uncertain,
include every plausibly affected suite.
**2. Run the selected command and preserve its exit status.**
For the Rails example:
```bash
set -o pipefail
EVAL_JUDGE_TIER=full EVAL_VERBOSE=1 bin/test-lane --eval test/evals/<suite>_eval_test.rb 2>&1 | tee /tmp/ship_evals.txt
```
Use the native command for other stacks. Respect the project's concurrency and
retry policy. Rails suites sharing a test lane run sequentially; stop on the
first failure before starting another paid suite.
**Long eval suites (30+ min): launch detached so a turn boundary can't kill them.**
Use the detached runner and eval lock; set its outer timeout to cover the
project's declared suite duration and retries. Do not change individual eval
limits. For a suite whose full bound fits 5400 seconds:
```bash
~/.claude/skills/gstack/bin/gstack-detach --label ship-evals --lock gstack-evals --timeout 5400 -- <project eval command>
```
Poll the printed log for `### gstack-detach EXIT=<code> ###`. Silence is not
success. Retain every configured attempt; skipped or unstarted cases do not
satisfy coverage.
**3. Check results and save evidence for Step 19.**
- **If any eval fails:** Do not rerun the full gate. Show failures and available
costs, then Read `~/.claude/skills/gstack/ship/sections/measure.md` and run its
loop for each red case: classify, measure alone, fix at the cause, re-measure,
then gate once. **STOP** only at its named-red stop.
- **If all selected evals pass:** Record actual counts, any reused evidence and
its source, and available costs. Continue to Step 7.
---