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

Automate a review finding as a lint or test: Both doors: add src/tui/ screen for the roadmap and its how-to as docs/features/ #12370

Merged opened this

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

Merged

The "both doors" check from #10628 already existed, but it let through the cases these four reviews describe. I tightened it so those cases now fail. Nothing in the diff touches web pages, so the tightened rule hasn't been exercised on a real pull, and the forge's full check hasn't run yet.

shipped production is serving it deploy cee6610

opened this

The "both doors" check from #10628 already existed, but it let through the cases these four reviews describe. I tightened it so those cases now fail. Nothing in the diff touches web pages, so the tightened rule hasn't been exercised on a real pull, and the forge's full check hasn't run yet.

The rule says a new page in src/web/pages/*.ts needs a terminal screen, a docs/features/<name>.md how-to, or a No door: <why> commit line. It accepted any new file anywhere under src/tui/ as the screen, including helpers and orphans nothing opens, so a pull could pass with no real screen.

What changed

  • src/kernel/both_doors.ts:

- A screen now has to be a new file in src/tui/screens/. A helper under src/tui/ui/ no longer counts. - scripts/gate.ts also checks that another module under src/tui imports the new screen, normally via its palette entry. If nothing does, the check says no entry opens that screen. - The failure message still names rule "both doors" and says which door is missing.

  • AGENTS.md: the rule's one line now says the screen lives in src/tui/screens/ and is reachable through its palette entry.
  • test/both-doors.test.ts: a new test covers a helper that isn't a screen and a screen nothing imports. The message-text assertions were updated for the new wording.

Checks run: test/both-doors.test.ts passes (4 tests), bun scripts/gate.ts is clean, and tsc --noEmit is clean.

What I chose not to do

  • Require the screen or doc name to match the page name. Existing pairs diverge (a roadmap screen for the boards page, gpx against geojson), so it would refuse good pulls.
  • Catch a new section added to an existing page, as in #12307. A diff can't tell a section from an edit, so that stays with reviewers. The No door: waiver is unchanged.

To try it: add src/web/pages/x.ts and docs/features/x.md with only a new src/tui/ui/x.ts, then run bun scripts/gate.ts. It refuses with rule "both doors".

Closes #12369

5 earlier comments · 1 event
commented

The "both doors" check from #10628 already existed, but it let through the cases these four reviews describe. I tightened it so those cases now fail. Nothing in the diff touches web pages, so the tightened rule hasn't been exercised on a real pull, and the forge's full check hasn't run yet.

The rule says a new page in src/web/pages/*.ts needs a terminal screen, a docs/features/<name>.md how-to, or a No door: <why> commit line. It accepted any new file anywhere under src/tui/ as the screen, including helpers and orphans nothing opens, so a pull could pass with no real screen.

What changed

  • src/kernel/both_doors.ts:

- A screen now has to be a new file in src/tui/screens/. A helper under src/tui/ui/ no longer counts. - scripts/gate.ts also checks that another module under src/tui imports the new screen, normally via its palette entry. If nothing does, the check says no entry opens that screen. - The failure message still names rule "both doors" and says which door is missing.

  • AGENTS.md: the rule's one line now says the screen lives in src/tui/screens/ and is reachable through its palette entry.
  • test/both-doors.test.ts: a new test covers a helper that isn't a screen and a screen nothing imports. The message-text assertions were updated for the new wording.

Checks run: test/both-doors.test.ts passes (4 tests), bun scripts/gate.ts is clean, and tsc --noEmit is clean.

What I chose not to do

  • Require the screen or doc name to match the page name. Existing pairs diverge (a roadmap screen for the boards page, gpx against geojson), so it would refuse good pulls.
  • Catch a new section added to an existing page, as in #12307. A diff can't tell a section from an edit, so that stays with reviewers. The No door: waiver is unchanged.

To try it: add src/web/pages/x.ts and docs/features/x.md with only a new src/tui/ui/x.ts, then run bun scripts/gate.ts. It refuses with rule "both doors".

commented

I merged main into the branch, resolved the conflict, and pushed the result (fdedd59bf).

  • Conflict: it was only in AGENTS.md. I kept my reworded "both doors" line, with the screen under src/tui/screens/ and imported by another module. I also kept the new "before/after looks" line that came in from main.
  • Gate: the first gate run after the merge flagged three over-long comment lines in src/kernel/both_doors.ts. I rewrapped them to 100 columns. The rewrapped version is what the gate passed on a later run. Before that run I had pushed 8906baceb with the gate still red.
  • Checks: bun scripts/gate.ts is clean and bun x tsc --noEmit is clean. test/both-doors.test.ts passes (4 tests). I ran only that file, not the full suite.

The last gate run printed nothing, which I took as a pass. I didn't print its exit code, and the first run's long-line message still appeared in an earlier tail of the same output, so the push may deserve a look at the forge's own check.

commented

The check failed because of a test my change broke: test/both-doors-refusal.test.ts. I fixed it and pushed (45de54f0a). I ran that file and test/both-doors.test.ts locally and they pass (7 tests). bun scripts/gate.ts and tsc --noEmit both exit 0. I did not run the full suite.

Cause: the passing fixture in "a range that adds the page with both doors passes" added a screen that nothing imports. The tightened rule now rejects that, and the check log shows this was the only red test not also red on main.

Fix:

  • Fixture: it now includes src/tui/screens/entries.ts, which imports the new screen, so it counts as reachable.
  • New test: a screen that nothing imports is refused, and the message says which entry is missing.

The other failures in that log, such as first-screen-line, are red on main as well, so they aren't this pull's. The 16 failed tests I can't account for individually. The log names only two, and I didn't reproduce the rest.

Changes requested — before the latest push

The rule is tightened in the right direction, and the message names rule "both doors". But the new reachability check can be passed by things that are not an entry, and it can crash the gate. Fix these in one round:

  1. The importer test matches on basename only (from "…/<name>.ts"). src/tui/ui/traffic.ts importing ./traffic.ts, or any unrelated x/traffic.ts import, counts as opening the screen. Resolve the import path to the screen file, or check the palette registry, which is the entry AGENTS.md names.
  2. Any other module under src/tui/ counts as an entry, including another orphan new screen. Two new screens that import each other pass. Require the importer to be the palette/entry module (or an existing file, not one added in the same pull).
  3. The regex only matches double-quoted static from "..." imports; single quotes and import("...") are missed, so a real entry can be refused.
  4. readdirSync("src/tui") throws if the directory is missing, which would crash the gate. Guard it.
  5. Style: the dynamic await import("node:fs") inside the function and the inline walker belong in a small module named for the concept (e.g. src/kernel/tui_entry.ts), with its own unit test of the resolution cases above, not in scripts/gate.ts.
  6. The imported parameter is optional, so the pure function silently skips the entry check when it is omitted. Make it required, or document why not.

Tests: the refusal tests cover only the orphan and the happy path. Add cases for items 1 to 3. The preview shows no web change, so there is nothing to try there.

commented

All six review points are fixed, and bun scripts/gate.ts, tsc --noEmit and the three both-doors test files (17 tests) are green. I ran no wider suite.

What changed

  • New src/kernel/tui_entry.ts: the reachability check now lives here, out of scripts/gate.ts (item 5). It has three parts:

- specifiers reads double-quoted, single-quoted, bare import "x" and dynamic import("x") forms (item 3). - resolveSpecifier resolves each relative path against its importer and compares it to the screen's real path. ui/traffic.ts importing ./traffic.ts no longer opens screens/traffic.ts (item 1). - readTui returns nothing when src/tui is missing, so the gate no longer crashes there (item 4).

  • Who counts as an entry: the importer must be a file that already existed before the pull, and not the screen itself. Two new screens importing each other are refused (item 2).
  • imported is required: the bothDoorsProblems parameter in src/kernel/both_doors.ts is no longer optional (item 6).
  • scripts/gate.ts: it now just calls these helpers.

Tests

  • New test/tui-entry.test.ts covers quote styles, dynamic imports, resolution, the same-basename helper, the same-pull importer and an absent src/tui.
  • test/both-doors-refusal.test.ts gains cases for the helper, the pair of new screens, single-quoted and dynamic imports, and a repository with no src/tui before the pull.
  • The happy-path fixture now has entries.ts in the base commit, because an entries file added in the same pull no longer counts.
  • The unit tests in test/both-doors.test.ts now pass the required imported argument.

Not done: I did not restrict entries to entries.ts and forge.ts only. Any pre-existing file under src/tui that imports the screen by its real path counts. That keeps the rule from refusing the real entry points (local/*, ssh-tell.ts and others) while still closing the orphan and basename holes. Say if you want it narrowed.

There is no web change, so nothing to try on the preview.

commented

The gate failure is fixed and pushed as dbe7f6b5b. The comment above bothDoorsProblems in src/kernel/both_doors.ts is now wrapped under 100 columns.

bun scripts/gate.ts and bun x tsc --noEmit pass, and test/both-doors.test.ts passes (4 tests). The gate only passed after I committed the change, so it appears to read committed content. scripts/fast.ts returned exit 2 because scripts/gate.ts is in the diff, so I ran the covering test file by hand. I did not run the full suite.

This PR adds a rule to the both-doors check: a pull that adds a page now also needs an added terminal screen that something opens. It comes from the #12369 finding: a screen nobody can open is not a door. A pull that adds a page still needs a how-to under docs/features/ as well, or a No door: line in a commit message.

commented

I found no fault in this pull, so I changed no code. I merged the latest main into the branch and pushed it so the check runs again.

Why the check failed. Five of the six failing files (spawn-small-cell, spawn-move-cell-e2e, spawn-cell-e2e, boundary-tenancy-cell, mirror-worker) are also red on main at bbd5a0b0c, and the check did not count them against this pull. The sixth, test/forge-origin-retry.test.ts, was green on main and red here. I think that failure is a flake, not something this pull causes:

  • The check log shows egress refused: 127.0.0.1:37751 is a private destination, which is a sandbox network refusal.
  • Those tests pass in my sandbox, where I don't have the real cells the check uses.
  • The pull doesn't touch anything near that file. It changes both_doors, tui_entry, gate.ts, AGENTS.md and their tests.

What I ran before pushing.

  • bun scripts/gate.ts and bun x tsc --noEmit both came back clean.
  • both-doors, both-doors-refusal, tui-entry and forge-origin-retry all passed, 19 tests in total.
  • The cell-based tests are skipped in my sandbox, so I could not run them here.

If forge-origin-retry fails again on the new push, its log will show whether the cause is the forge.json egress allowlist or something in the test itself.

Approved

All earlier asks are met in the diff: resolved-path matching (a helper with the screen basename no longer opens it), importers must pre-exist the pull, quote and dynamic import forms are read, an absent src/tui no longer crashes the gate, the logic lives in src/kernel/tui_entry.ts, and imported is required. Each has a test that would fail without it, and the failure message names rule "both doors". No web change, so nothing to see in the preview. Next two things I would do: (1) Non-blocking: run the tightened rule against the next real page pull and note whether the any-existing-tui-file importer rule is too loose. (2) Non-blocking: consider handling export * from and re-export-only barrels explicitly in a test.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs