Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 3 additions & 5 deletions .claude/skills/pre-push-gate/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ prints each stage as it starts, so the running command is the other reliable
answer.

It runs **every check** GitHub CI runs (which additionally runs `npm install`,
and runs `coverage` as a parallel job), plus two local-only steps. So the
and runs `coverage` as a parallel job), plus one local-only step. So the
direction that matters holds: **passing `local:gate` locally means every check
CI applies has already passed on your machine** — the strongest predictor of a
green CI there is here, though not a proof (a different OS, and the bare test
Expand Down Expand Up @@ -189,16 +189,14 @@ own.
for a measurement that needs contention; it does not get a result sooner,
because the queued run finishes before an overlapped one would.

## Local-only steps
## Local-only step

Two stages have no GitHub CI counterpart, each deliberately:
One stage has no GitHub CI counterpart, deliberately:

- **`smoke:web:firefox`** — the three browser-driven web smokes again under
Firefox. Trialled as a CI job and removed (#2086): across a dozen runs it never
disagreed with Chromium, and `playwright install --with-deps` carries a real
flake surface. Kept in front of a human about to push instead.
- **`smoke:tui`** — needs a real TTY. It _is_ invoked in CI via `npm run smoke`
and self-skips there on `process.env.CI`, so it needs no guarding.

A guard (`scripts/lib/workflow-gate.mjs`, run by `npm run test:scripts`) fails
the suite if a workflow invokes a `local:*` script, a non-Chromium engine pass,
Expand Down
8 changes: 5 additions & 3 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,9 @@ jobs:
# boots the prod web bundle in headless chromium (#1615); smoke:web:app
# goes further and drives connect → open app → widget ready against a
# composable MCP App server (#1859). Both reuse the chromium installed
# above. smoke:tui self-skips here — the Ink TUI needs a real TTY (raw
# mode) that headless CI lacks, so its boot/render check is local-only.
# above. smoke:tui runs for real here too (#2408): it gives the Ink TUI
# a pseudoterminal through util-linux script(1), which ubuntu-latest
# ships, and pins CI=false for the child so Ink renders interactively.
run: npm run smoke

- name: Run Storybook play-function tests
Expand Down Expand Up @@ -260,7 +261,8 @@ jobs:
- name: Verify the publishable tarball end to end
# Builds, `npm pack`s, installs the tarball into a clean consumer, and
# drives the installed bin. Needs registry access to pull the tarball's
# runtime deps — available here. `smoke:tui` inside it self-skips on CI.
# runtime deps — available here. Its TUI check is `--tui --help` only;
# the real TUI boot is `smoke:tui`, which the `build` job runs.
run: npm run pack:verify

- name: Publish to npm (single package, with provenance)
Expand Down
4 changes: 2 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -512,12 +512,12 @@ free to run a full `local:gate`). Raising a budget nobody chose hides no race.
## Mandatory pre-push gate

- **ALWAYS run `npm run format` before committing.** The **root** `format` auto-fixes `core/`, the root `scripts/` tooling, the root shared surface, and every client's scope in one shot. `validate` runs the non-fixing `format:check` and will fail in CI on any unformatted file, so run the auto-fixer first rather than letting `format:check` catch it.
- **`npm run local:gate` is the mandatory pre-push command.** It runs **every check** `.github/workflows/main.yml` runs, plus two local-only steps, so a green run here is the strongest predictor of a green CI this repo has: every check CI applies has already passed on your machine. It is not a proof — CI runs on a different OS and, since #2341, runs each client's suite bare where the gate runs it only instrumented — so the one residual is a test that passes *only* when slowed down, which is a race (#1596) to fix, never headroom to keep. Expect several minutes.
- **`npm run local:gate` is the mandatory pre-push command.** It runs **every check** `.github/workflows/main.yml` runs, plus one local-only step, so a green run here is the strongest predictor of a green CI this repo has: every check CI applies has already passed on your machine. It is not a proof — CI runs on a different OS and, since #2341, runs each client's suite bare where the gate runs it only instrumented — so the one residual is a test that passes *only* when slowed down, which is a race (#1596) to fix, never headroom to keep. Expect several minutes.
- **The gate runs each client's test suite once, instrumented; CI runs it twice (#2341).** CI's `build` job runs the bare `test` inside `validate` and its parallel `coverage` job runs `test:coverage`, which costs CI no wall clock. Serially in one process the bare pass was ~80s of a ~370s gate, re-running exactly the files the coverage pass runs a few minutes later — so the gate calls **`local:validate`**, which is `validate` minus each client's `test` leg (every client's `validate` is `check && test`, and `local:validate` runs the `check` half). "Every check" is therefore a claim about checks, not invocations, and it holds because `@vitest/coverage-v8` collects coverage from V8's own profiler (`Profiler.takePreciseCoverage`) and rewrites no source: a test file sees identical code either way, and the instrumented run is only *slower*, which makes it the stricter of the two for the failure class this repo actually sees (a correct test cut off under load). A test that passed only *because* it ran slower would be a race — #1596's class, a defect wherever it surfaces — and CI's bare pass still runs it. **`npm run validate` is unchanged**, because CI runs it directly and it is the inner-loop check; `verify:*` guards that ask "is this reachable from `validate`" are unaffected. `local:validate` lives in the `local:` namespace so the workflow guard keeps it out of CI by construction.
- **`npm run validate` is the fast inner-loop check and is NOT an acceptable substitute.** It runs `test`, not `test:coverage`, so it does **zero** coverage gating, no smokes, and no Storybook tests. Skipping the gate is how a push passes every fast local check and still fails CI.
- **Concurrent gates queue; they do not overlap.** `local:gate` takes a machine-wide lease (`scripts/gate-lease.mjs`, #2339) so a gate started in a second worktree waits for the first rather than running alongside it, and queued gates start in arrival order (#2473). Overlap is not merely slow — the web smokes bind fixed ports, so two gates reaching the same smoke together go red on a diff that cannot have caused it (measured: one of two concurrent gates failed at 279s on port 6298 while a quiet gate passed in 257s). The wait names the holder and its worktree; a holder that dies releases within 30s (unless its lock directory cannot be removed, in which case the wait runs to its 45-minute cap and names the path); `INSPECTOR_SKIP_GATE_LEASE=1` bypasses it, which is for a measurement that _needs_ contention, never for getting a result sooner — the queued run finishes sooner anyway.
- There is deliberately **no `npm run ci`** — that name collided with the `npm ci` built-in, which clean-installs from the lockfile and does not run this script.
- What each stage covers, and why two of them are local-only, is [`docs/quality-gate.md`](./docs/quality-gate.md); how to diagnose a failing stage is the `pre-push-gate` skill.
- What each stage covers, and why one of them is local-only, is [`docs/quality-gate.md`](./docs/quality-gate.md); how to diagnose a failing stage is the `pre-push-gate` skill.

## Waiting on long-running work

Expand Down
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,10 +94,10 @@ Each client self-validates from its own folder; the root scripts chain them. The
```bash
npm run validate # fast inner loop: format:check + lint + typecheck + build + unit tests
npm run coverage # the per-file ≥90% gate (lines/statements/functions/branches)
npm run local:gate # MANDATORY before pushing — every GitHub CI check, plus two local-only ones
npm run local:gate # MANDATORY before pushing — every GitHub CI check, plus one local-only one
```

`npm run local:gate` chains every check below, plus the smokes and the Storybook tests. [Testing and the quality gate](./docs/quality-gate.md) owns the stage list and says what each one covers and why two are local-only; [`AGENTS.md`](./AGENTS.md) holds the testing rules themselves.
`npm run local:gate` chains every check below, plus the smokes and the Storybook tests. [Testing and the quality gate](./docs/quality-gate.md) owns the stage list and says what each one covers and why one is local-only; [`AGENTS.md`](./AGENTS.md) holds the testing rules themselves.

## Contributing — `AGENTS.md`, `CLAUDE.md`, and the skills

Expand Down
6 changes: 3 additions & 3 deletions clients/launcher/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,9 +100,9 @@ through the built launcher artifact (beyond the `--help` checks in
The second half is the assertion (#2147). Spawned with its stdin on
`/dev/null`, Ink cannot enter raw mode for `useInput`, so the TUI painted one
frame and exited 1 about 40ms later — and this smoke, which settled OK on the
first frame, won that race and reported success on every machine. It is
local-only (self-skips under `CI`), which is precisely where a false green
goes unnoticed.
first frame, won that race and reported success on every machine. It runs in
GitHub CI as well as the local gate (#2408); it pins `CI=false` for the
child, since Ink suppresses interactive frames when it detects CI.

Both rebuild `test-servers/build` on **every run** — once per process, whether
or not it already exists (#2111). Presence is not freshness: a smoke driving a
Expand Down
Loading
Loading