Skip to content

Optimizing and documenting repo-hygiene processes: rename the local pre-push gate off the name ci, and guard it out of CI #2146

Description

@cliffhall

Problem

The root ci script is not what runs in CI, and the name actively works against us.

ci: validate && coverage && verify:build-gate && verify:bundle-externals
    && smoke && smoke:web:firefox && ci:storybook

The smokes themselves do run in GitHub CI, and should — the workflow runs npm run smoke, which covers smoke:launcher, smoke:cli, smoke:tui, smoke:web and smoke:web:chromium. That is not in question here.

What must never run in CI is narrower: the non-Chromium engine passes. smoke:web:firefox (#2086) is deliberately absent, and smoke:web:webkit fails two of the three smokes outright. Confidence in the cross-engine runs is not high enough to let them break CI.

smoke:tui is a third, separate case and is not in that category: it is invoked in CI via smoke and self-skips there (process.env.CI), because the Ink TUI needs a real TTY. It handles itself and needs no guarding.

GitHub CI runs validate, coverage, the two verify gates, smoke and test:storybook — never the root ci script itself.

Three distinct problems follow from the name:

1. It collides with a built-in npm command. npm ci is clean-install-from-lockfile. It does not run the ci script — verified. So npm ci and npm run ci are one keystroke apart, do entirely different things, and the wrong one fails by succeeding at something else: several minutes of reinstalling node_modules instead of running the gate, with no error to tell you.

2. It invites the cross-engine passes into CI by accident. This is the one that matters. A future contributor editing .github/workflows/main.yml who sees a script called ci has every reason to think it belongs there — or to "fix" a workflow by changing npm ci to npm run ci. Either would silently pull smoke:web:firefox into GitHub CI. The maintainer position is that the cross-engine passes are always the local pre-push gate, period — confidence in them is not high enough to let them break CI, and smoke:web:webkit fails two of the three outright.

3. It muddles the concept every time it is documented. Six review rounds on #2133 kept turning up prose that blurred "GitHub CI" and "the local gate". A script literally named ci for the local-only thing guarantees that keeps happening.

Scope

Widened from a pure rename to repo-hygiene process work, since it touches ~40 references across 15 files and the concepts they describe.

1. Rename the gate, with no alias

ci → local:gate (suggestions welcome, but it should say local).

No back-compat alias. Deliberate: an alias preserves exactly the association being scrubbed, and leaves something copy-pasteable for a workflow author. Removing it outright means npm run ci fails with Missing script: ci and npm prints the available scripts — a good failure that teaches the right name.

⚠️ Avoid prepush:gate. npm pre/post hooks match the exact script name, so prepush:gate is the pre-hook of push:gate and fires automatically if anyone ever adds one — verified. Plain prepush is safe (it does not hook push:gate), but local:gate avoids the class entirely and reads as the counterpart to GitHub CI.

Rename ci:storybook too — it is also local-only (the workflow calls npm run test:storybook from clients/web directly, not this wrapper). Something like local:storybook or fold it into the gate.

Commit history and older PR bodies will reference npm run ci. That is fine; history is a record of what was true then.

2. Guard that the gate can never drift into CI

The rename removes the invitation; a check removes the possibility. Add a test:scripts assertion over .github/workflows/** that fails on:

  • an invocation of the gate itself (local:gate);
  • an invocation of a non-Chromium engine pass (smoke:web:firefox, smoke:web:webkit);
  • an invocation of smoke:web:engine, whose engine comes from the environment and so cannot be read off the workflow at all;
  • setting SMOKE_BROWSER to anything other than chromium — the back door that would redirect an otherwise innocent npm run smoke.

It must NOT forbid npm run smoke, smoke:web:chromium, or smoke:tui. Those belong in CI and are there today; smoke:tui self-skips under process.env.CI on its own. The guard is about engines, not about smokes.

This is the durable half. Everything else here is prose that can rot; this cannot.

3. Document the two-tier model in one place

Right now the CI-vs-local split is described in README.md, AGENTS.md, .github/copilot-instructions.md, three client READMEs, and several script headers — which is why it keeps drifting. One canonical table (what runs in GitHub CI, what runs only locally, and why each local-only step is local-only), with the others pointing at it.

Per the mirror rule, AGENTS.md and .github/copilot-instructions.md change in the same PR.

4. Sweep the stale references

~40 across README.md, AGENTS.md, .github/copilot-instructions.md, clients/{web,cli}/README.md, docs/inspector-roadmap-2026-h2.md, and header comments in scripts/*.mjs + one test.

Acceptance criteria

  • No root script is named ci or ci:*; npm run ci fails with npm's missing-script error.
  • A test:scripts check fails if any workflow file invokes the gate, a non-Chromium engine pass, or smoke:web:engine, or sets SMOKE_BROWSER to a non-Chromium value — while continuing to allow npm run smoke and smoke:web:chromium, which belong in CI.
  • Every doc that names the gate uses the new name and distinguishes GitHub CI from it.
  • AGENTS.md and .github/copilot-instructions.md updated together.
  • No change to what any gate actually runs — this is naming, guarding and documentation.

Notes

Came out of #2133, where smoke:web:firefox was deliberately placed in the local gate rather than GitHub CI after a trialled CI job was removed for never disagreeing with Chromium. That decision is only as durable as the thing stopping someone from undoing it by accident, which today is nothing but a comment.

Activity

  1. added this to the v2.5.0 milestone on Aug 26, 2026
  2. added
    v2Issues and PRs for v2
    choreMaintenance: deps, build tooling, CI, cleanup — no user-facing behavior change
    on Aug 26, 2026
  3. cliffhall commented on Aug 26, 2026

    @cliffhall
    MemberAuthor

    Triage: Priority Medium (total 6)

    • Severity 2 — nothing is broken; the cost is confusion and a latent hazard, and knowing npm ci ≠ npm run ci is a workaround
    • Urgency 3 — wanted this milestone. The hazard is concrete rather than theoretical: the local-only steps are one plausible workflow edit away from running in GitHub CI, and one of them (WebKit) fails outright
    • Bonuses: +1 milestone

    Not scored higher despite the "must never run in CI" framing: the guard in scope item 2 is what makes that durable, and until someone actually edits the workflow, nothing is currently wrong.

  4. cliffhall commented on Aug 26, 2026

    @cliffhall
    MemberAuthor

    Correction — the scope of the guard was wrong as first written. Body updated.

    @cliffhall: "I do want smoke tests to run in CI (they do now, right?) just with chromium only. It's the firefox and safari ones I'm not certain of."

    Right on both counts, and I had conflated three different things into one list.

    Yes — the smokes run in GitHub CI today. The workflow runs npm run smoke, which is smoke:launcher && smoke:cli && smoke:tui && smoke:web && smoke:web:chromium. Chromium only. That is the intended state and nothing here should change it.

    The three categories, kept separate now:

    In GitHub CI? Guard it?
    smoke:launcher, smoke:cli, smoke:web, smoke:web:chromium ✅ yes, and should be No
    smoke:tui ✅ invoked via smoke, self-skips there (needs a real TTY) No — it handles itself
    smoke:web:firefox, smoke:web:webkit ❌ never Yes

    My first draft listed smoke:tui alongside the engine passes. That was wrong: it is already in CI and no-ops there by design, so forbidding it would both misdescribe the status quo and block someone legitimately touching it later.

    The guard is about engines, not smokes. It now fails on: invoking local:gate; invoking smoke:web:firefox / smoke:web:webkit; invoking smoke:web:engine (env-driven, so the engine cannot be read off the workflow at all); or setting SMOKE_BROWSER to a non-Chromium value — that last one being the back door that would redirect an otherwise innocent npm run smoke. It explicitly must not forbid npm run smoke, smoke:web:chromium, or smoke:tui.

    Acceptance criteria updated to match.

  5. self-assigned this
    on Aug 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

choreMaintenance: deps, build tooling, CI, cleanup — no user-facing behavior changev2Issues and PRs for v2

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions