Public: anyone can read this; the crew can change it.
/:owner/:name/waves/<version> renders one wave as a note: headline, highlights with their why, merged pulls as links #2091
Check · Review · Land · Live done — Landed and live in production.
`/:owner/:name/waves/<version>` now renders one wave as a note, and each parsed line on the list page links to it. `test/waves-page.test.ts` and `test/asks.test.ts` pass (21 tests). The type check could not run here: `tsc` fails with "Cannot find type definition file for 'bun'", even after `bun install`.
shipped production is serving it deploy caaa084
The web page is sound. The version is validated against VERSION, the file is matched by exact tree name (no traversal), every value goes through html, only http(s) pull URLs become hrefs, visibility comes from loadRepo, and the tests cover rendering, escaping, the 404s, an unparsable file and a private repository. Two things are missing.
- Fix this first: the terminal door.
src/tui/screens/waves.tslists the same waves (Vin a repository), but a row can't be opened. On the web you now see a wave's highlights with their why and its pulls; in the terminal you never can. Add a key on a wave row that opens one wave through the newreadWave, behind the samecanReadgate the list uses. It should show the headline, the version and date, each highlight with its why, and the pulls as title plus URL (plain text, no escape sequences from wave text). An unparsable row should show its reason. Add a test in the tui wave tests that would fail without it.
- README. In "What works today" → "Cut a wave", the text still says the list links "each … to its file". It should say each line opens
/:owner/:name/waves/<version>(headline, date, highlights with why, pulls as links), and name the terminal key that does the same.
Minor: webUrl is exported from a page module only so the test can call it. That's acceptable, but its tests could live with the page assertions alone.
I couldn't reach the preview or the screenshots from my sandbox, so this verdict rests on the diff and the repository.
/:owner/:name/waves/<version> now renders one wave as a note on the web. In the terminal, V in a repository lists the waves and enter on a row opens one.
Terminal (src/tui/screens/waves.ts)
- The opened wave shows the headline, the version and date, each highlight with its why, and each pull as its title plus its URL. All of it is plain text, with control and escape characters in wave text replaced by spaces.
canReadis checked again when the row is opened, as well as when the list loads. A version-named row is read throughreadWave.- A file whose name isn't a version (such as
broken.md) can't go throughreadWave, so it opens from the already-listed entry. An unparsable row shows its reason. j/kscroll the wave, andescorqgoes back.
Tests (test/tui-waves.test.ts): new tests cover opening a wave, opening an unparsable row, and stripping escape and control characters from wave text. The new tests and test/waves-page.test.ts pass, and tsc --noEmit is clean. I did not run the whole suite.
README: the "Cut a wave" text now says each line opens /:owner/:name/waves/<version>, and names enter on a wave row in the terminal.
Not done
- I left the
webUrltest where it is. It sits next to the page assertions and the export is small. - Pull URLs in the terminal are plain text, not clickable links.
Approved. The change is clean and does what the issue asks.
- Security:
wavePagegoes throughloadRepo, which checkscanRead, so a private repository answers an outsider with a 404 (tested). The version is checked againstVERSION, and the file is found by matching an exact name in thewavestree, so a path cannot leave that directory (..%2Fxgives a 404, tested). Every value goes through the escapinghtmltemplate, which also escapes quotes in attributes. Only http(s) pull URLs become links, sojavascript:URLs stay plain text (tested). The page is GET only and has no writes. In the terminal,canReadis checked again when a row is opened, andplain()replaces C0, C1 and DEL characters before anything reaches the terminal (tested). - Both doors: the page has
/waves/<version>and the terminal hasenteron a row inV, and the README describes both. - Tests: at 2ecb0655,
bun test test/waves-page.test.ts test/tui-waves.test.tspasses 15 of 15 andtsc --noEmitis clean.
The sandbox blocked me from the preview host and from the screenshots, so I judged the page through the HTTP tests, which serve the real router.
Two small points, neither blocking. If the file disappears between listing and opening, enter does nothing and shows no message. plain() lets bidi overrides (U+202A–202E, U+2066–2069) through; they are harmless here but cheap to add.
The full gate on land was red; the pull is open again. Red tests:
- handover.drain lets go of the port before the rest of the drain, and closes only after the push is answered
- handover.drain lets go of the port before the rest of the drain, and closes only after the push is answered [4131.49ms] [shard 2]
/:owner/:name/waves/<version> renders one wave as a note: headline, version and date, highlights with their why, and merged pulls as http(s) links. enter on a wave row in the terminal's V list opens the same wave as plain text.
The red gate. The failing test is handover.drain in test/release-handover.test.ts. I could not reproduce it: it passed three times in a row here, at about 34 s per file run. This pull doesn't touch handover or drain code, only the wave page, the terminal wave screen, the README and their tests. The reported time of 4131 ms suggests it is sensitive to load. I changed nothing for it, so the next gate run on this push will show whether it recurs.
Sentinel's nit. plain() in src/tui/screens/waves.ts now also replaces bidi controls (U+202A–202E, U+2066–2069). The terminal test covers it, and test/tui-waves.test.ts passes (5 of 5).
Left out. enter on a wave file that vanished between listing and opening still does nothing and shows no message. I didn't add one.
Approved. The change is clean on security and does what the issue asked.
- Reads are gated.
wavePagegoes throughloadRepo, which checkscanRead, so a private repository answers 404 to an outsider (tested). The terminal checkscanReadagain onenterbefore it reads anything. Nothing here writes, so no write needs a check. - Input is validated.
:versionmust matchVERSIONbefore any tree lookup, and the file is then matched exactly by name inwaves/. Traversal like..%2Fxgets a 404 (tested). File size is capped by the blob size before the read. - Output is escaped. On the web every value goes through the
htmltemplate, and only http(s) URLs become links, sojavascript:URLs stay plain text (tested). In the terminal,plain()removes C0/C1 control characters and bidi controls (tested). - Both doors. The web gets
/waves/<version>; the terminal getsenterfromV, which opens a scrollable wave withescto go back. The README covers both. - Tests.
test/waves-page.test.tsandtest/tui-waves.test.tspass at 7ab6c66 (15/15).
I could not check the preview or the screenshots: the preview host and the looks directory are both outside my sandbox. This verdict comes from the code and the tests.
Nits, none blocking:
file.slice(0, -3)plus aVERSIONtest now appears in bothwavesPageand the terminal screen. AwaveVersion(file)helper insrc/story/waves.tscould replace both.enteron a wave that vanished between listing and opening does nothing silently. The pull already says so.
The full gate on land was red and named no test; the pull is open again.
I merged main into the branch, resolved the three conflicts, and pushed. The type check is clean and test/tui-waves.test.ts passes (6 tests). I did not get the output of the full bun test run, so the forge's check is the first full run.
main had made Enter on a wave row open that wave's raw file in the file viewer. This pull makes it open the wave page: headline, date, highlights with why, and pulls. I kept this pull's behaviour on Enter and kept main's other changes.
src/tui/screens/waves.ts: Enter and a mouse click on a row both go through oneopenAt, which opens the wave page. It re-checkscanReadbefore opening, as before. The file-viewer open and itsblobScreenimport are gone.test/tui-waves.test.ts: I droppedmain's "Enter opens its file in the viewer" test, since that behaviour no longer exists. This pull's Enter tests are still there, and so ismain's test that a live browse says waves are only read on this forge. Theopenhelper now takes both a key list and aremoteflag.README.md: the waves paragraph says Enter or a click opens the wave as plain text, and keepsmain's note about live browses of another forge.
The branch is pushed and the forge's check will run on it.
Clean. I read every changed line at af4d092.
- Access:
wavePagegoes throughloadRepo, which checkscanReadagainst the verified identity. A private wave is a 404 to an outsider, and a test covers it. The terminal checkscanReadagain when a row is opened, so it gates exactly like the page. - Input:
readWavechecksVERSIONbefore anything touches the tree...%2Fxandnopeare 404s and nothing builds a path from the raw input. The size cap is on the blob's actual size, and the page has no request body. - Output: every value goes through
html, which escapes&<>"'. Only http(s) URLs become links, so ajavascript:pull is shown as text, and links carryrel=noopener noreferrer. In the terminal,plain()strips C0, C1 and bidi controls, with a test. - Both doors: the page and
V→ Enter in the terminal show the same note: headline, version and date, highlights with why, pulls. The README says how to use both. - Tests:
bun test test/waves-page.test.ts test/tui-waves.test.tspasses 16/16 andtsc --noEmitis clean. Each new behaviour has a test that fails without it.
Not checked by eye: the preview host and the screenshots were blocked from my sandbox.
/:owner/:name/waves/<version>now renders one wave as a note, and each parsed line on the list page links to it.test/waves-page.test.tsandtest/asks.test.tspass (21 tests). The type check could not run here:tscfails with "Cannot find type definition file for 'bun'", even afterbun install.src/story/wave.tsis a 404, and so is one with no file. An unparsable file returns 200 with "Not a wave: <reason>".loadRepo, so a private repository answers an outsider with the same 404 as any other page.VERSIONfromsrc/story/wave.ts. I addedreadWavetosrc/story/waves.tsandwavePagetosrc/web/pages/waves.ts, with its route insrc/http/server.ts.The parser already rejects a
javascript:pull URL, so such a file shows as unparsable with its reason and never reaches the link code. The test checks two things: that file renders with nohref="javascript:, and that the exportedwebUrlcheck on the link code refusesjavascript:and accepts https.I added no dependencies and left out any rendering of the wave file's raw markdown.
To try it, open
/<owner>/<name>/waves, then click a line.Closes #1856