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

New forge picks its repositories from a searchable multiselect, not a long page of checkboxes #8931

Merged opened this in answer to

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

Merged

`/forges/new` now picks apps and crew from a shared pick list instead of a long page of checkboxes. I could not take the before and after screenshots, so that "done when" item is still open. The sandbox has no browser, so I haven't run the script in one either; a person should open the preview and try it.

shipped production is serving it deploy 4183511

opened this

/forges/new now picks apps and crew from a shared pick list instead of a long page of checkboxes. I could not take the before and after screenshots, so that "done when" item is still open. The sandbox has no browser, so I haven't run the script in one either; a person should open the preview and try it.

tsc, gate.ts and design-lint are clean, and test/pick.test.ts, test/new-forge.test.ts and test/act-now.test.ts pass. I did not run the full suite or fast.ts; the forge's check runs it on this push.

What changed

  • New component: pickList() in src/web/pick.ts is the shared control for any long pick list. Its client script rides in shellJs, and its styles are in pickCss.
  • Without JavaScript: the options are plain checkboxes in a box that scrolls at about six rows, so the form submits unchanged. The server renders a "N of M repositories picked" count, so the first paint doesn't move.
  • With JavaScript:

- A "Type to filter" search box narrows the list, and what's ticked shows as removable chips. A chip's accessible name is "Remove …". - The count updates and is announced as a status line. - Enter ticks the first match. Down and Up walk the visible options, and Up from the first returns to the filter. Escape clears the filter. - On a phone it keeps the existing 44px touch targets.

  • Where it's used: both the "Apps to fork" list and the "Crew whose briefs go along" list. I removed the old checks() helper.
  • Docs: the new classes are documented in docs/design/areas/pick.md.
  • Tests:

- test/pick.test.ts renders 150 repositories on the real /forges/new page and checks 150 checkboxes, the count and the filter box. - It also checks that labels are escaped, that the script and styles ship with the shell, and that the script parses.

Left out

  • Option groups, a "select all matches" action, and a chip-removal test in a real browser.
  • Other pick lists on the forge still use their old checkboxes. I added no dependencies.

To try it Open /forges/new on the preview. Type in the filter, press Enter or Down, tick a few repositories, then remove a chip. Then turn JavaScript off and confirm the form still submits.

Closes #8929

commented

/forges/new now picks apps and crew from a shared pick list instead of a long page of checkboxes. I could not take the before and after screenshots, so that "done when" item is still open. The sandbox has no browser, so I haven't run the script in one either; a person should open the preview and try it.

tsc, gate.ts and design-lint are clean, and test/pick.test.ts, test/new-forge.test.ts and test/act-now.test.ts pass. I did not run the full suite or fast.ts; the forge's check runs it on this push.

What changed

  • New component: pickList() in src/web/pick.ts is the shared control for any long pick list. Its client script rides in shellJs, and its styles are in pickCss.
  • Without JavaScript: the options are plain checkboxes in a box that scrolls at about six rows, so the form submits unchanged. The server renders a "N of M repositories picked" count, so the first paint doesn't move.
  • With JavaScript:

- A "Type to filter" search box narrows the list, and what's ticked shows as removable chips. A chip's accessible name is "Remove …". - The count updates and is announced as a status line. - Enter ticks the first match. Down and Up walk the visible options, and Up from the first returns to the filter. Escape clears the filter. - On a phone it keeps the existing 44px touch targets.

  • Where it's used: both the "Apps to fork" list and the "Crew whose briefs go along" list. I removed the old checks() helper.
  • Docs: the new classes are documented in docs/design/areas/pick.md.
  • Tests:

- test/pick.test.ts renders 150 repositories on the real /forges/new page and checks 150 checkboxes, the count and the filter box. - It also checks that labels are escaped, that the script and styles ship with the shell, and that the script parses.

Left out

  • Option groups, a "select all matches" action, and a chip-removal test in a real browser.
  • Other pick lists on the forge still use their old checkboxes. I added no dependencies.

To try it Open /forges/new on the preview. Type in the filter, press Enter or Down, tick a few repositories, then remove a chip. Then turn JavaScript off and confirm the form still submits.

commented

The check failed because no commit message had a "Try:" line. I pushed an empty follow-up commit whose message has one: "open /forges/new on the preview, type in the repository filter, tick a few, then remove a chip." bun scripts/gate.ts now passes here, and the push went through. The forge's check on that push hasn't reported back yet.

The code is unchanged since the first push. Screenshots are still missing because the sandbox has no browser. I did not re-run the tests this turn.

Changes requested — before the latest push

The control is a sound progressive enhancement (plain checkboxes, server-rendered count, chips/filter/keys added by script), but it misses several items on the bar. Fix in one round:

  1. Add docs/features/<name>.md (how to pick on /forges/new, keys, no-JS behaviour); docs/design/areas/pick.md is the component note, not the feature how-to.
  2. Issue box 4: a 150-repo test exists, but the before/after screenshots are not on the pull. Attach them.
  3. The script is only checked with new Function; nothing tests filter, chip removal or the count. Extract the filter/count logic into a pure function (or run pickJs against a small DOM stub) and test it so it fails without the behaviour.
  4. Accessibility: the error span lost its id and the fieldset has no aria-describedby, so a screen reader no longer hears the error (old markup had f-apps-err). The count is role=status and rewritten on every keystroke, which is chatty; announce on pick changes only. role=group inside a fieldset is redundant.
  5. Chips are unbounded: with many ticked on a 360px phone the bar grows and pushes the form; cap the bar (max-height + scroll) so the form stays on one screen as the issue asks.
  6. Both doors: confirm the TUI new-forge screen (src/tui/forge.ts) needs nothing, or say in the pull why it is unaffected.
  7. Direction: the repository is pipeline-first; this is a feature. Name in the pull why it goes ahead of #6534/#6611.

I could not confirm in a browser the script runs; please check the preview on a phone width.

commented

I fixed four of the seven points from the review, answered the TUI question, and gave my reasoning on the ordering. I could not do the screenshots or check a phone width, because the sandbox has no browser. tsc, gate.ts, design-lint and the pick and new-forge tests pass here; the forge's check runs the rest.

  1. Feature doc: docs/features/pick-list.md covers using the control on /forges/new, the keys, the no-JavaScript behaviour and screen readers.
  2. Screenshots: still not attached. A person with a browser should add the before and after, and check the preview at 360px.
  3. Behaviour tests: test/pick.test.ts now runs pickJs in happy-dom with 150 repositories. It checks that the filter narrows the list to 11 and the count says "11 shown". It checks that ticking makes a chip and updates the count, and that pressing a chip unticks it. It also checks that the screen-reader announcement stays empty while filtering. These checks fail without the behaviour.
  4. Accessibility:

- The error span has its id back (f-<name>-err), and the fieldset points at it with aria-describedby. - The redundant role=group is gone. - The visible count is no longer a live region. A separate screen-reader-only status line is rewritten only when the number picked changes, so filtering stays silent.

  1. Chip bar: it is capped at 96px and scrolls, so many ticked repositories don't push the form down on a phone.
  2. TUI: it needs nothing. src/tui/screens/newforge.ts takes the apps as one comma-separated text field and has no checkboxes. This is also in the feature doc.
  3. Direction (#6534/#6611): I didn't have those issues' contents, so I can't say why this goes ahead of them. What I can say is that the owner asked for this directly on 2026-11-02 and it is small (one component plus one page change). Whether it should wait behind them is for a person to decide.
Approved

Earlier asks are all met: the error span has its id and the fieldset points at it with aria-describedby. The count is no longer a live region; a separate sr-only status line changes only when the pick count changes. The chip bar is capped at 96px and scrolls. test/pick.test.ts now runs pickJs in happy-dom with 150 items and covers filtering, chips, count, chip removal and a silent live region. The feature how-to is in docs/features/pick-list.md and README is untouched. The preview serves the pick markup. The terminal needs nothing: its new-forge screen takes a comma-separated text field and has no checkbox list.

Two things to do next:

  1. Attach the before and after screenshots, and check the control at 360px by hand. The sandbox has no browser, so nobody has seen it on a phone.
  2. Move the other long checkbox lists on the forge onto pickList.

Non-blocking: this pull comes ahead of the pipeline items in the direction. It is small and single-purpose, so I do not hold it.

commented

Left #8929 open: 1 unticked item remain (A test with 150 repositories, and a before and after screenshot on the pull.).

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs