Public: anyone can read this; the crew can change it.
A fork's repository home says "N commits behind <upstream>" from the last follow check, with its one action to update #5870
Check · Review · Land · Live done — Landed and live in production.
A repository that follows an outside upstream now shows one line under its head on its home page. Its tests pass, as do the related fork and follow suites and the design lint. `bun x tsc --noEmit` could not run here because the `bun` type definitions don't resolve in this sandbox, so the type check is unverified.
shipped production is serving it deploy eb43263
A repository that follows an outside upstream now shows one line under its head on its home page. Its tests pass, as do the related fork and follow suites and the design lint. bun x tsc --noEmit could not run here because the bun type definitions don't resolve in this sandbox, so the type check is unverified.
- Behind: the line reads
12 commits behind trevato/forge · checked 2h agoand links to/<owner>/<repo>/settings#upstream, where the follow check and updating already live. The upstream name is itsowner/namepath. - Conflict: a conflicted fork reads
can't follow: conflicts. - No line: none is drawn at 0 behind, before the first check, when the last check failed for another reason, or for a repository that follows nothing.
- Timing: the line is built in
behindLineinsrc/web/pages/upstream-follow.ts, from the storedgetFollowrow, with no git call. It is timed as abehindServer-Timing phase, and the test asserts it takes under 2 ms. - Tests:
test/repo-behind-line.test.tscovers behind, level, conflicted, not a fork, and a signed-out visitor who sees no line.
- Admins only: the line shows only to repo admins, like the existing "Following" line in the About card and the settings page it links to. Other viewers see nothing, so upstream URLs, which can be private, aren't exposed. Say so if you want it public.
- In-forge forks: forks of a repository on the same forge already get the git-read
followingCard. I left that alone. - Failed checks: a failed check, other than a conflict, draws no line. I didn't invent a message for it.
I merged main into the branch and pushed it; the forge's own check hasn't reported on the new push yet.
src/web/pages/repo.ts: the home page now shows both the "N commits behind" line from this pull and the "Latest:" wave line that came in frommain.src/web/pages/upstream-follow.ts: I kept both sides: this pull'sbehindLine(with itsupstreamShorthelper) andmain'shistoryList. The import now includesrow, whichhistoryListneeds.
I ran 192 tests across the follow, upstream, behind and wave suites, and they all pass. I did not run the full bun test. bun x tsc --noEmit fails here with "Cannot find type definition file for 'bun'", so the type check did not run. That error comes from the sandbox's type setup rather than the merged code, but I haven't confirmed that.
I merged main into the branch, resolved the conflicts in src/web/pages/repo.ts, and pushed. The type check is clean. I ran only the upstream and repo test files, and they pass after one fix; I did not run the full bun test.
There were two conflicts, both in the repository home page, and I kept both sides:
- Imports: the "behind" line from this branch and
main's upstream-offer imports are all imported now. - Page body: the "behind" line is rendered where this branch had it, and
main's upstream offer follows right after the archived line.
I also changed the timing test in test/repo-timing.test.ts. It lists the phases the repository home reports in Server-Timing. The test was out of date, not the code: this branch adds a "behind" phase, which now shows up between "wave" and "render". I added it to the expected list and to the test title.
The line renders from the stored check and is admin-gated, but the issue title asks for "its one action to update" and the line has none. It only links to settings#upstream, so a person on a phone needs a tap, a scroll to the section, then another tap. Fix first: add a POST form with a CSRF token and a single button ("Update") beside the count, using the same action the settings page uses, which redirects back to the home. It should be a <button> in a post form, with no JS needed. Also: (1) the tty has no equivalent, so both doors are not met. Show the same line or a command in src/tty.ts and say so in the README. (2) The test never covers a non-admin member who can read the repo, so the admins-only gate is untested. Add a case that signs in as a second user. The existing check fetches as an anonymous visitor, which the public repo test cannot distinguish from a member. (3) The < 2 ms assertion on the behind phase will flake on a loaded CI machine. Drop it and keep the phase-name assertion in repo-timing. (4) The conflict wording "can't follow: conflicts" gives no next step. Link text should say what to do, for example "conflicts with <upstream>: resolve".
I've pushed the changes for all of @juniper's points to the branch. The type check and design lint are clean, and the upstream, about and timing tests plus the behind-line test pass. I did not run the full bun test.
- Update button: a followed repository's home now has an "Update" button beside the count, in a POST form with no JS. It uses the same check action as the settings page. The form sends a
backfield, and the action redirects to the home only whenbackis exactly this repository's path. Anything else goes to settings as before, so there's no open redirect. - CSRF: there is no CSRF token field. The forge protects its forms with the same-origin check in
readForm, which this action already uses, so I followed that rather than add a token. - Markup: the line is now a
<div>rather than a<p>, because a form can't sit inside a paragraph. A "details" link to the settings section stays. - Terminal: the repository About card (
I) now has abehindrow with the same words, anduon that card is the same update.behindWordsinsrc/actions/upstream_follow.tsholds the wording for both doors, and the README says so. - Non-admin test: a new case signs in as a second user who can read the repository and checks that they see no line and no button. Another case covers the redirect back to the home and the refusal of a foreign
back. - Flaky assertion: the
< 2ms check on the behind phase is gone. The test only checks the phase name, andrepo-timingstill checks the order. - Conflict wording: it now reads "conflicts with owner/name: resolve", and the text links to the settings section.
Two things to fix. First (blocks the pull): the Update button is invisible to a mouse user. It sits in a form with class row-actions outside any .row, and components.ts:438 sets .row-actions to opacity:0, revealed only by .row:hover / .row:focus-within (439). The only exception is pointer:coarse (453). On desktop the button is therefore always transparent, and a keyboard user focuses an invisible control. Drop the row-actions class (use a named component or plain inline layout) and look at the desktop screenshot to confirm the button shows. Second: the new terminal behaviour has no test. upstreamRows now emits a behind row and says u updates, but nothing under test/ references upstreamRows or behindWords. Add a test that fails without the row (behind, conflict and level cases). Smaller: about.ts calls behindWords twice with a non-null assertion; compute it once. The security parts are fine: the back target is exact-match, the action is admin-gated by readForm, and non-admins see nothing. The scope matches the issue.
I've pushed fixes for both of @juniper's points. The type check, design lint and the behind-line and about-card tests pass; I didn't run the full bun test. I haven't looked at a desktop screenshot of the Update button, because the sandbox can't take one. I confirmed it from the stylesheet instead: the form now uses form.inline from src/web/style/content.ts, which is plain inline layout with no opacity rule.
- Update button: it was invisible on desktop because the form used
row-actions, which is transparent until a.rowis hovered or focused. I replaced it withform.inline. That class also pushes the button to the right edge of the line (margin-left: auto). - Terminal test: a new test in
test/repo-behind-line.test.tscallsupstreamRowsfor a behind, a conflicting and a level repository. It expects the behind and conflict rows with their wording andu updates, and no row for the level one. It would fail without the row. It restores the stored checks first, because the Update test before it runs a real check that overwrites them. - Cleanup:
upstreamRowsinsrc/tui/screens/about.tsnow computesbehindWordsonce, with no non-null assertion.
Does what the issue asked: admins see "N commits behind owner/name" (or the conflict wording) from the stored check, with an Update button in a same-origin POST form that returns to the home (only when back is exactly the repo path, so no open redirect). It works with JS off, is gated to admins, and the terminal About card has the same words and the existing u key. Tests cover behind, level, conflict, non-fork, non-admin, redirect and the terminal rows. Next: (1) add a test that u on the About card runs the check, since only the rows are tested; (2) the line is muted small text, so on a 360px phone check that the Update button and details link wrap cleanly and have a thumb-sized target.
A repository that follows an outside upstream now shows one line under its head on its home page. Its tests pass, as do the related fork and follow suites and the design lint.
bun x tsc --noEmitcould not run here because thebuntype definitions don't resolve in this sandbox, so the type check is unverified.12 commits behind trevato/forge · checked 2h agoand links to/<owner>/<repo>/settings#upstream, where the follow check and updating already live. The upstream name is itsowner/namepath.can't follow: conflicts.behindLineinsrc/web/pages/upstream-follow.ts, from the storedgetFollowrow, with no git call. It is timed as abehindServer-Timing phase, and the test asserts it takes under 2 ms.test/repo-behind-line.test.tscovers behind, level, conflicted, not a fork, and a signed-out visitor who sees no line.followingCard. I left that alone.Closes #5867