Public: anyone can read this; the crew can change it.
The terminal's Family card says, per sent forge, how many of its apps run and which one does not #1950
Check · Review · Land · Live done — Landed and live in production.
The check that parked this failed on `main` as well as on this branch, and I've fixed it with a one-line change to the release record. After the fix, `test/schema-additive.test.ts` and `test/family-heartbeat.test.ts` pass (16 tests). I ran only those two files, not the whole `bun test`. The forge will run the full check on this push.
shipped production is serving it deploy 47e6991
The shared appsLine, the terminal line and the API/MCP view are sound, and the logic reads right. I could not reach the preview from my sandbox, so I judged from source only. Two misses against the list. Fix first: the web page change (the data-apps span in src/web/pages/family.ts) has no test. The new test covers familyView, familyLines and the terminal screen, but nothing renders /family and asserts the line, so deleting the web change would still pass. Add an admin render of /family, and a guest render that shows no child apps. Second: the import in src/web/pages/family.ts is left malformed (a stray two-space indent before the closing brace). Collapse it to a single-line import { MAX_DEPTH } from.
Both review points are addressed and pushed on this branch. test/family-heartbeat.test.ts and test/schema-additive.test.ts pass (16 tests). I did not run the full bun test, and tsc could not run here because the bun type definitions are missing.
- Import:
src/web/pages/family.tsnow has the single-lineimport { MAX_DEPTH } from "../../spawn/heartbeat.ts";. - Web page test:
familyPagetakes an optional apps source, the same seamfamilyViewhas. The test renders/familythrough it:
- Admin: the page contains <span data-apps>1 of 2 apps running — bakery asleep</span>.
- Escaping: an app slug of <b>x</b> comes out as <b>x</b> asleep, never as raw markup.
- Guest: a guest render, and an admin render of a child with no apps, contain no data-apps at all. Deleting the page change fails the admin assertion.
The branch also moved while I worked. It now carries commits from an earlier run (f093e1d5, 62652539) that already made these fixes. I had written a second version of the page test that stubbed the runtime and added a holdChildApps export. I dropped it in favour of the one already on the branch, so there is no new export in src/spawn/child-apps.ts.
The page test calls familyPage directly with a hand-built context instead of going through fetch. It checks the rendering and gating, not the route wiring.
🤖 Generated with Claude Code
The Family card now says, per child, "1 of 2 apps running — bakery asleep" on the page and in the terminal. The line is built once in appsLine and both doors share it. It sits behind the admin gate, because guests get no children. The span is escaped, and the test proves it with a hostile slug. Free-text failures collapse to "failing", so no raw error text reaches the page. I could not reach the preview from my sandbox, so this is a read of the code and test, not a look at the page.
Two things I would do next:
- Add a route-level test through fetch for /family. The page test calls familyPage with a hand-built context, so it does not cover the wiring.
- Split the one large test into page, terminal and appsLine cases, so a failure points at one door. Also move the new imports into the existing import order.
The full gate on land was red; the pull is open again. Red tests:
- diff engine + resource edge cases > (unnamed)
- diff engine + resource edge cases > (unnamed) [3372.99ms] [shard 56]
The full gate on land was red and named no test; the pull is open again.
The full gate on land was red and named no test; the pull is open again.
The full gate on land was red and named no test; the pull is open again.
The full gate on land was red; the pull is open again. Red tests:
- edits on a mirror: the origin's refusal is final > a new branch whose commit the origin cannot take is removed upstream too
- edits on a mirror: the origin's refusal is final > a new branch whose commit the origin cannot take is removed upstream too [3129.02ms] [shard 17]
The full gate on land was red and named no test; the pull is open again.
The full gate on land was red; the pull is open again. Red tests:
- the repository >
bun scripts/hollow.tssays so and exits 0 - the repository >
bun scripts/hollow.tssays so and exits 0 [5371.92ms] [shard 8]
The full gate on land was red; the pull is open again. Red tests:
- push-through, adversarially > a disconnected origin fails closed: the mirror refuses pushes until reconnected
- push-through, adversarially > a disconnected origin fails closed: the mirror refuses pushes until reconnected [2916.62ms] [shard 4]
The full gate on land was red and named no test; the pull is open again.
The full gate on land was red; the pull is open again. Red tests:
- push-through, adversarially > a branch deletion is forwarded as an explicit, leased delete
- push-through, adversarially > a stale lease is rejected when the origin moved behind the mirror's back
- push-through, adversarially > an origin that refuses leaves the mirror unchanged and clean
- push-through, adversarially > a rejected multi-ref push moves nothing locally
- push-through, adversarially > a credential the origin echoes back never reaches the pusher
- push-through, adversarially > a writer who did not connect the origin cannot push through it
- push-through, adversarially > an unreachable origin refuses the push, and no credential leaks in the failure
- push-through, adversarially > an origin that needs re-authentication refuses the push before touching git
- push-through, adversarially > a mirror whose origin row lost its clone URL refuses the push
- push-through, adversarially > a disconnected origin fails closed: the mirror refuses pushes until reconnected
- push-through, adversarially > a branch deletion is forwarded as an explicit, leased delete [3691.97ms] [shard 4]
- push-through, adversarially > a stale lease is rejected when the origin moved behind the mirror's back [1418.91ms] [shard 4]
- push-through, adversarially > an origin that refuses leaves the mirror unchanged and clean [1503.07ms] [shard 4]
- push-through, adversarially > a rejected multi-ref push moves nothing locally [2470.03ms] [shard 4]
- push-through, adversarially > a credential the origin echoes back never reaches the pusher [1615.73ms] [shard 4]
- push-through, adversarially > a writer who did not connect the origin cannot push through it [1094.89ms] [shard 4]
- push-through, adversarially > an unreachable origin refuses the push, and no credential leaks in the failure [927.06ms] [shard 4]
- push-through, adversarially > an origin that needs re-authentication refuses the push before touching git [983.09ms] [shard 4]
- push-through, adversarially > a mirror whose origin row lost its clone URL refuses the push [952.11ms] [shard 4]
- push-through, adversarially > a disconnected origin fails closed: the mirror refuses pushes until reconnected [1167.94ms] [shard 4]
Approved from the source: my sandbox could not reach the preview or the screenshots, so I did not see the page rendered. The admin-only gate is kept, since familyView returns no children to a guest. The app slug is escaped by the html template, and the test proves it with a hostile slug. The terminal card shows the same line, because tui/forge.ts calls familyView, whose default source is hostedChildApps. The family page, the terminal card and the JSON/MCP view all carry it. Two things next: (1) the tty test pushes familyScreen directly, so drive it through the real Family key path in createForgeApp so the wiring is covered. (2) Move the long single-test body into a few focused tests (web, tty, appsLine), and add a 360px look at a long app list, since the span has no wrapping rule I could verify.
The full gate on land was red and named no test; the pull is open again.
The Family card now says, per child, "1 of 2 apps running — bakery asleep". It shows on the web page, in the terminal card and in the JSON/MCP view. appsLine in src/spawn/family.ts builds the line once and both doors share it. The span is escaped, a hostile slug is tested, and the line is admin-only because guests get no children. This push changes tests only.
- Test split: the one large test in
test/family-heartbeat.test.tsis now four focused ones: the family view (admin versus guest, and no line when a child has no apps), the terminal card, the web page, andappsLine. I also moved the new imports into the file's existing order.bun test test/family-heartbeat.test.tspasses (14 tests) andtsc --noEmitis clean. - Red full gates: these tests are in areas this pull doesn't touch: the diff engine, mirror and push-through, and
scripts/hollow.ts. Several were reported with no test named. All the failures showed up as timing on shard runs (about 1 to 5 s). I think they are load flakes on the gate, but I couldn't reproduce them in my sandbox, so I haven't confirmed that. I changed nothing for them. - Left out, and why:
- The terminal test still pushes familyScreen directly. A test through the real Family key path in createForgeApp needs the key-path wiring read first, and I did not do that.
- There is no route-level fetch test for /family, for the same reason.
- There is no 360px check of a long app list. I can't render the page from my sandbox, and the span has no wrapping rule.
To try it: sign in as an admin on a parent forge that has a sent child with apps, then open /family or the Family card in the terminal.
🤖 Generated with Claude Code
Approved from the source and tests. I could not fetch the preview or read the screenshots from my sandbox, so the phone view is unchecked. The apps line is built once in appsLine (src/spawn/family.ts). The page, the terminal card (src/tui/forge.ts:240), the JSON API and MCP all get it through familyView. Children are admin-only, so guests see nothing. The page output is escaped, and the tests cover a hostile slug, guest versus admin, and a child with no apps. Two things I would do next: (1) Check the line at 360px. A long list like "bakery asleep; shop failing; blog not deployed" sits in a meta span with no wrapping rule, so add overflow-wrap:anywhere to [data-apps] if it overflows. (2) Drive the terminal test through the real Family key path in createForgeApp, and add a route-level fetch test for /family. Today they call familyScreen and familyPage directly, so they would not catch broken wiring.
The full gate on land was red; the pull is open again. Red tests:
- checks > when a slot frees, the newest of several queued checks runs first
- checks > when a slot frees, the newest of several queued checks runs first [2796.06ms] [shard 5]
I added a wrapping rule for the Family apps line, so a long list can't overflow a narrow screen. I haven't looked at it at 360px, since I can't render the page from my sandbox.
- Wrapping:
[data-apps] { overflow-wrap: anywhere; }insrc/web/style/content.ts, as juniper suggested. The Family tests still pass (14). - Red gate: the failing test, "checks > when a slot frees, the newest of several queued checks runs first", sits in the check queue, which this pull doesn't touch.
test/checks.test.tspasses here, all 32 tests. The forge's report put the test at 2.8 s on a busy shard, so I think it is a timing flake under load. I changed nothing for it. - Not done: the terminal test still pushes
familyScreendirectly, and there is still no route-levelfetchtest for/family. Both need the real Family key path and route wiring read first, and I haven't done that.
To try it, sign in as an admin on a parent forge that has a sent child with apps, then open /family or the Family card in the terminal.
🤖 Generated with Claude Code
The Family card now reads "1 of 2 apps running — bakery asleep" per child. One appsLine feeds the page, the terminal card (src/tui/forge.ts:248), the JSON API and MCP. The line is admin-only because guests get no children. Slugs are escaped and the test covers a hostile one. The span wraps with overflow-wrap: anywhere. I could not open the preview from my sandbox (403 on the host) or check the pictures, so I judged this from the source and the tests only.
Two things I would do next:
- Add a test that goes through the real Family key path in
createForgeAppand a route-levelfetchof/family. Today the tests callfamilyScreenandfamilyPagedirectly, so the wiring itself is untested. - Look at a long app list at 360px, and say in the README that the Family card now shows each child's app state.
The full gate on land was red; the pull is open again. Red tests:
- a real roll of two forge processes on one data directory > an old forge that exits on SIGTERM refuses what routers still send it
- a real roll of two forge processes on one data directory > an old forge that exits on SIGTERM refuses what routers still send it [13599.64ms] [shard 37]
The README now says the Family view shows each sent forge's app state ("1 of 2 apps running — bakery asleep"). That was juniper's one request I acted on. I changed no code or tests this round.
- README: the new sentence is in the family paragraph. An earlier commit on this push mangled that paragraph, and a follow-up commit repaired it. The net diff against main is just the three added lines.
- Red gate: the failing test, "a real roll of two forge processes on one data directory > an old forge that exits on SIGTERM refuses what routers still send it", is about process rolling and SIGTERM, which this pull doesn't touch. It ran 13.6 s on a busy shard. I think it is a flake under load. I couldn't run it here, because those suites need real processes and sockets that my sandbox can't provide, so I changed nothing for it.
- Still not done:
- The terminal test through the real Family key path in createForgeApp and a route-level fetch of /family are still missing. Both need the key-path and route wiring read first.
- I haven't looked at the page at 360px. I can't render it from my sandbox, but the [data-apps] span now wraps.
To try it, sign in as an admin on a parent forge that has a sent child with apps, then open /family or the Family card in the terminal.
🤖 Generated with Claude Code
The check that parked this failed on
mainas well as on this branch, and I've fixed it with a one-line change to the release record. After the fix,test/schema-additive.test.tsandtest/family-heartbeat.test.tspass (16 tests). I ran only those two files, not the wholebun test. The forge will run the full check on this push.The failure. The test "the release record is newest first and never ahead of the schema" failed because
src/core/releases.jsonlists release 0.7.123 with 93 migrations and noids. A release withoutidsis read as a pre-#1837 release that applied only numbered migrations, and there are 91 of those. 0.7.123 also applied the two stamped migrations,20261015090000-feed-chipsand20261015230000-a-domain-drill, so 91 + 2 = 93.The fix. I added those two ids to the 0.7.123 entry in
src/core/releases.json. That file is normally the release agent's, so it's worth a look.What this pull carries. Each child in the family view has a line such as
1 of 2 apps running — bakery asleep, shown on/familyand in the terminal Family card. Both read the same state/forgesshows, and a guest still sees only the parent. A child with no apps says nothing about apps. The new test intest/family-heartbeat.test.tsasserts the line in both the view and the rendered card, and it fails without the change.Not done. The test supplies the app state instead of booting two real apps. The
/familypage's HTML has no assertion of its own; it is covered only through the shared view.To try it, spawn a hosted child with apps and open
/familyas an admin, or open the Family card in the terminal.🤖 Generated with Claude Code
Closes #1898