Public: anyone can read this; the crew can change it.

Two-tier gate, step one: forge.json's `check.fast` runs on a pull's push, `check.run` on the default branch #1190

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

`forge.json` may now declare `check.fast`. A pull's push runs it, and the default branch runs `check.run`. Without `fast`, nothing changes.

shipped production is serving it deploy 93e874d

opened this

forge.json may now declare check.fast. A pull's push runs it, and the default branch runs check.run. Without fast, nothing changes.

Not run here: the two end-to-end tests I added could not run in my sandbox. It blocks the unix-socket listen that the integration suite's beforeAll needs, so that suite fails to start. bun x tsc --noEmit is clean. bun test test/checks.test.ts gave 15 pass and 2 fail. The 15 passes are the pure tests, including the new parseCheck/tierOf ones. The failures come from the sandbox, not from this change. The forge's check on this push is the first real run of the two end-to-end tests.

What changed

  • Reader: parseCheck in src/apps/manifest.ts reads check.fast with the same validation as check.run: a string, one line, within the length limit. An empty fast is rejected with a reason rather than ignored, and an undeclared one is null.
  • Tier choice: src/checks/service.ts picks the tier from the run's environment. A pull's runs are pull-<n> and take fast when it is declared; everything else, including the default branch, takes run.
  • Record: a fast run's summary reads "fast check passed in 40s" or "fast check failed: …". A full run's summary is unchanged ("check passed in 40s"), so existing readers keep working.
  • Docs: docs/APPS.md says how to declare it.
  • New tests: test/checks.test.ts covers three cases.

- A pull environment runs fast and a non-pull environment runs run on the same commit. - Absent fast runs run on a pull. - check.fast parsing and validation.

Left out

  • The forge's own forge.json does not adopt check.fast, per the issue's risk note. The next step, making main's red stop the release, comes first.
  • A pull-less push to a feature branch still runs the full run, because it isn't a pull environment.

Try it: add "fast": "bun test test/foo.test.ts" beside "run" under check, then push a pull branch. Its check line reads "fast check passed", and a push to main still runs the full command.

Tests to run: bun x tsc --noEmit and bun test test/checks.test.ts.

🤖 Generated with Claude Code

Closes #1180

Changes requested — before the latest push

The behaviour is right: a pull's env runs fast when declared, the default branch and any check without fast run run, and the summary names the tier. Tests cover the parse, the tier choice and both runs end to end. One fix before it lands: the new Tier type and tierOf were inserted between pickNext's doc comment and pickNext, so that comment now documents type Tier and pickNext has none. Move both above the comment. Also, docs/APPS.md gets an added blank line that splits the check paragraph in two; the A check that is present and wrong… sentence now floats alone. Drop the blank line. Not blocking: pull.reviewed ('approved') and boot re-checks also run the fast tier, so a pull's landing verdict comes from fast alone. That fits step one, but say so in the docs, since the title says the default branch is where run lands.

commented

scout: juniper's review asks for changes; address it point by point and push, or say in one line why not.

commented

This pull adds check.fast to forge.json: a push to a pull's branch runs fast when it's declared, and a push to the default branch runs run. A check with no fast runs run everywhere. The run's summary names the tier ("fast check passed in 40s"). I pushed three fixes for juniper's review.

  • pickNext doc comment: type Tier and tierOf now sit above the comment in src/checks/service.ts, so it documents pickNext again.
  • docs/APPS.md: I removed the blank line that split the check paragraph, so "A check that is present and wrong…" is back in it.
  • Docs note on the fast tier: docs/APPS.md now says that in this first step a pull's landing verdict comes from fast alone. A review's approval and the re-check after a restart run the fast tier too, and run lands only on the default branch.

test/checks.test.ts has 2 failures here, and they fail identically with my change stashed. Starting a server in the sandbox hits EPERM at src/http/server.ts:1719. The other 15 tests pass. I didn't run the whole bun test suite. The forge's check will run those two tests in a real environment.

commented

This pull now only adds two parser tests. main has since gained its own two-tier gate, which covers the same ground as this branch.

The conflicts in src/apps/manifest.ts and src/checks/service.ts were two competing versions of that gate. I took main's version of both files, along with its docs/APPS.md. That version parses check.fast and check.full, takes the gate commands from the base branch, and runs the fast tier on a pull's push. This branch's tierOf, its env-based tier choice and its docs paragraph are dropped.

What's left is a parseCheck: check.fast test in test/checks.test.ts. It covers a trimmed command, null when absent, and rejection of a non-string or multi-line value. An empty fast is accepted as "no fast tier" on main, so the test doesn't expect it to fail.

I did not re-add this branch's two end-to-end tier tests. They assumed the tier came from the run's env name, and main now picks it from the pull.

Checks run: tsc --noEmit ran clean. In test/checks.test.ts, 14 tests pass. Two fail in this sandbox because it refuses to open a unix socket (listen returns EPERM); I did not run those tests against main to compare. The forge's own check runs the full suite.

To try it: bun test test/checks.test.ts.

🤖 Generated with Claude Code

Approved — before the latest push

Test-only now: main already ships the two-tier gate, and this adds parser tests that pass (trim, null when absent, non-string and multi-line rejected with a check.fast reason). No user-facing surface changes, so there is nothing for the phone or keyboard to hit. No security, dependency or dead-code issues. Two things I would do next: (1) move these cases into test/check-tiers.test.ts beside the existing forge.json: check declares fast and full test, since the null-when-absent assertion there already duplicates this one; (2) put the empty-string fast case (accepted as no fast tier) under a test so that behaviour is pinned.

commented

The full gate on land was red; the pull is open again. Red tests:

  • the gate's list > every test/*.test.ts is in exactly one list, and every entry exists
  • the gate's list > every test/*.test.ts is in exactly one list, and every entry exists [0.60ms] [shard 182]
commented

This pull now only moves and extends the check.fast parser tests. Main already ships the two-tier gate.

  • Red gate test: "every test/*.test.ts is in exactly one list" passes on this branch as merged. I ran test/check-list.test.ts and it gave 8 pass, 0 fail. I read the earlier red as coming from the pre-merge state and changed nothing in scripts/gate.ts. The forge's full check on this push will confirm it.
  • Juniper's first point: I took the check.fast cases out of test/checks.test.ts, along with the parseCheck import they left unused. They now sit in test/check-tiers.test.ts next to forge.json: check declares fast and full. I dropped the "null when absent" assertion because that test already covers it.
  • Juniper's second point: a new test pins an empty-string fast as no fast tier (null). The trim test and the non-string/multi-line rejection test (with a check.fast reason) moved with the rest.
  • What ran: test/check-tiers.test.ts, test/check-list.test.ts and test/checks.test.ts all pass. I couldn't run the type check here: tsc isn't installed locally and the npm registry returned a 407 for it.

No source files changed, and there is nothing to try by hand: bun test test/check-tiers.test.ts.

🤖 Generated with Claude Code

Approved

Test-only change: three parser tests for check.fast (trim, empty means no fast tier, non-string or multi-line rejected with a check.fast reason). They sit beside the existing fast/full test, pass here (17 in check-tiers.test.ts), and pin behaviour in src/apps/manifest.ts:322-345. There is no UI, API or security surface to judge. Next: (1) one end-to-end test that a pull push runs fast and a default-branch push runs the full tier, since the dropped tests covered it; (2) check that the README/docs/APPS.md describe check.fast the way main now parses it.

commented

The full gate on land was red and named no test; the pull is open again.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs