Public: anyone can read this; the crew can change it.
A child forge shows its size and usage, and the parent's machines page shows each child's usage against its size #10875
Check · Review · Land · Live done — Landed and live in production.
A child forge running in a cell now shows its own size and what it is using, and the parent forge's `/system` page lists each child's use against its size. The terminal's System command shows the same. The new tests and the existing System, spawn and forges suites pass, as do the type check, `scripts/gate.ts` and `scripts/design-lint.ts`.
shipped production is serving it deploy 3a0780e
A child forge running in a cell now shows its own size and what it is using, and the parent forge's /system page lists each child's use against its size. The terminal's System command shows the same. The new tests and the existing System, spawn and forges suites pass, as do the type check, scripts/gate.ts and scripts/design-lint.ts.
- How the child learns its size:
cellEnv(src/spawn/cell.ts) now puts the child's size and its cgroup folder into the child's environment, asFORGE_CELL_SIZEandFORGE_CELL_CGROUP_DIR. To get the folder, a cell now exposes it ascgroupDir(src/apps/contain.ts) when the kernel enforces the cell's limits. - How the child measures itself:
selfUse(src/spawn/usage.ts):
- Memory comes from memory.current.
- CPU is the change in cpu.stat's usage_usec divided by the time between two readings at least a second apart; the first reading is an average since the process started.
- Disk reuses the existing background walk of the data folder, kept for 30 s.
- If the cgroup can't be read, memory and CPU fall back to the forge's own process only, and the page and terminal say so.
- A forge that isn't a cell child shows nothing new.
- Parent side:
childrenUselists each finished child with its size and its use, read the same way as/forges. A child sharing the parent's process reads "shared with this forge"; a stopped cell reads "not running". - Both views:
/systemgets two sections, "This forge's size" and "Child forges". The terminal getsselfLinesandchildLines, which wrap whole figures rather than cutting off the usage.
To keep the terminal command drawing straight away, as the existing System-command test expects, I added listChildrenNow in src/actions/spawn.ts: it reads the list of children without waiting. It shares one ledger-to-child mapping with listChildren. The last CPU reading is kept in process memory, and that is noted in AGENTS.md as the repo asks. How-to is in docs/features/forge-size-use.md.
Not done here: the figures are read when the page loads and are not part of the live 2-second updates, and there is no history chart. I couldn't update the plan on the issue because no keep_plan tool was available in this run.
Tests are in test/spawn-self-use.test.ts. They check the figures against hand calculations (0.5 cores, then 1.5 cores; 1.5 GiB of 4 GiB; 512 MiB of 10 GiB) and the process-only fallback. They also check both sides through a real /system request and the terminal System command. The forge's script that picks tests covering a change named almost the whole suite, so I chose and ran the spawn*, client*, forges*, system-*, tui-system and system-telemetry files myself. Tests for unix sockets and real cells are skipped in this sandbox; the forge's check runs them.
To try it: as the owner, open /system on a forge that has made children to see "Child forges". On a child running in a cell, the same page shows "This forge's size".
🤖 Generated with Claude Code
A child forge now shows its own size and use, and the parent's /system lists each child's use against its size (#10853). On /system, a forge that made children has a "Child forges" section with each child's memory, CPU and disk used against its limits. A forge running inside a cell (a sandboxed process with its own limits) shows "This forge's size". The terminal's System screen says the same.
- Where the numbers come from:
src/spawn/usage.tsreads them, from the kernel's cgroup files when the cell has a cgroup. To make that possible,src/apps/contain.tsnow records the cell's cgroup directory (cgroupDir).src/spawn/cell.tsandsrc/actions/spawn.tspass that through to the pages. - Pages: the new sections are in
src/web/pages/system.tsandsrc/tui/screens/system.ts. - Tests:
test/spawn-self-use.test.tscovers the readings and both views. - The earlier red check: the two failures were a size limit in the forge pill's script (24,802 bytes against a 24 KB cap). That was already over on main and unrelated to this change. It is fixed on main in d980f0f43 (#10876), and this branch now merges main in.
The gate, the type check, the design lint, and the pill, spawn-use and System tests all pass locally (68 tests).
Not done: there is no history chart for each child; only the latest reading is shown.
To try it: as the owner, open /system on a forge that has spawned a child, or run System in the terminal.
🤖 Generated with Claude Code
Changes needed (round 1, all in one list):
- src/web/pages/system.ts, childrenSection (principle 8, plain words): a stopped cell renders "memory not running · cpu not running · disk not running" in the page. useWords gives "not running" for each figure and the row wraps it in 'memory … · cpu … · disk …'. The test enshrines it (
expect(html).toContain("memory not running")). Say just "not running" for an idle child, as the terminal's childLines already does, and fix the test.
- src/web/pages/system.ts, selfSection (principles 1 and 8): the footnote paragraph explains itself twice. It restates the size already in the rows, and it names
memory.current,cpu.statand 'cgroup', which is jargon for a person. Cut it to one plain sentence, e.g. "Set by the forge that made this one. Disk is measured every 30 seconds." Keep the process-only caveat in plain words ("memory and CPU count this process alone"). The cgroup file names belong in docs/features/forge-size-use.md, where they already are.
- src/web/pages/system.ts, childrenSection (principles 3 and 6/7, tokens): the row's meta hand-builds a meter, a second span and a trailing size in one row, so each child row carries three things plus a 'Forges' link paragraph. Use the named row/meter components the way meterRow does (the figure beside the meter, size in trail) and drop the separate
<p><a href="/forges">Forges</a></p>. Make the title the link to the child instead.
- src/tui/screens/system.ts and src/actions/spawn.ts: listChildrenNow duplicates listChildren's directory walk with sync fs. Share one parse step (readLedger's validation) rather than casting
JSON.parse(...) as Ledger, so an invalid ledger is rejected the same way in both. As written the TUI accepts files the page would skip.
- Test gap: nothing asserts a non-owner gets neither section (the gate the list asks for). Add a guest/non-admin GET /system case. Also the idle row is asserted only with the wrong wording from point 1.
- Preview: I did not find a cell child there, so "This forge's size" is shown only by unit tests. State in the pull how you checked it on a real cell, or that the check ran skipped.
What is fine: no secret, cgroup path taken only from the parent's environment and only read, no README change, docs/features how-to present, terminal entry kept admin-only, AGENTS.md shared-state note added.
A forge that runs in a cell of its own ("child forge") now shows its size on /system. A parent lists each child it made with that child's usage against its size. Only the owner sees either section. This round addresses @juniper's six points:
- Wording for a stopped child: the row now says just "not running", as the terminal does. It used to say "memory not running · cpu not running · disk not running". The test checks for the right wording and fails on the old one.
- Footnote: it is now one sentence: "Set by the forge that made this one. Disk is measured every 30 seconds." When the cell's memory figures can't be read, it adds "Memory and CPU count this process alone." The size name moved into the heading ("This forge's size: medium"), so the footnote no longer repeats the figures in the rows. The cgroup file names now appear only in
docs/features/forge-size-use.md. - Child rows: each one uses the standard row component. The title is the child's name, linking to the child (only for
http(s)addresses). The meta shows a memory meter for a child in a cell, then one short phrase. The trail shows the size. The separate<p><a href="/forges">link is gone.ChildUseRownow carriesurlfor the link. - One ledger check: a new
parseLedgerinsrc/actions/spawn.tsaccepts a ledger only when it is JSON with a stringname, a stringbodyIdand a booleandone.readLedger, and solistChildren, uses it, and so does the terminal'slistChildrenNow. A test puts an invalidbad.jsonnext to a good ledger and checks that both readers skip it. - Owner-only sections: the page did not actually gate the two sections. Any signed-in person could see them. They are now checked with
canSeeMemory, the same check as the memory block and the terminal's System command. A new test signs in as a non-admin person and finds neither section onGET /system. - Checking on a real cell: not done. My sandbox can reach only this app's production and previews, and both run as parent forges, not cell children. So "This forge's size" is shown only by tests:
selfUseagainst a fake cgroup tree, andGET /systemwithFORGE_CELL_SIZEset. The suites that run real cells were skipped here; the forge's check runs them on this push.
Checks run before pushing: bun scripts/gate.ts, bun x tsc --noEmit and bun scripts/design-lint.ts pass. scripts/fast.ts named 2,031 test files for this diff, so I picked the ones covering it: spawn* (core, sizes, api, anon, placement, cell), forges-fit, every system-*, tui-system and spawn-self-use. 166 passed, 0 failed, 5 skipped.
To try it, open /system as the owner of a forge that has made children. Each child's name links to it and the size is at the end of the row. A child in a cell has a memory meter, and a stopped one says "not running". Open the same page as anyone else: neither section appears.
🤖 Generated with Claude Code
A forge that runs in a cell of its own ("child forge") now shows its size and how much of it it uses on /system and in the terminal's System command. A forge that made children lists each one with its usage against its size. Only the owner sees either section.
- A child forge: it shows "This forge's size: small" (or medium, large), then a meter row each for memory, CPU and disk against that size. A footnote reads "Set by the forge that made this one. Disk is measured every 30 seconds." When the cell's figures can't be read, it adds "Memory and CPU count this process alone." The size and the cgroup directory come from the parent through environment variables (
FORGE_CELL_SIZE,FORGE_CELL_CGROUP_DIR), and the cgroup files are only read. - A parent forge: "Child forges" has one row per finished child. The child's name links to it, the size is at the end of the row, and a child in a cell has a memory meter. A hosted child reads "shared with this forge" and a stopped cell reads "not running".
- Owner-only: both sections are checked with
canSeeMemory, the same check as the memory block and the terminal's System command. Anyone else who opens/systemsees neither. - One ledger check: a new
parseLedgerinsrc/actions/spawn.tsis the validation both the page'slistChildrenand the terminal'slistChildrenNowuse. A file that is not JSON, or lacksname,bodyIdordone, is skipped the same way by both. - Merge with
main: the only conflict was the imports insrc/spawn/cell.ts. It now imports bothSIZE_IO(the disk-bandwidth cap that landed on main in #10867) and this pull'sCGROUP_DIR_ENV/SIZE_ENV.
How the code is laid out:
- The reading itself is in
src/spawn/usage.ts. - The page sections are
selfSectionandchildrenSectioninsrc/web/pages/system.ts. - The terminal lines are
selfLinesandchildLinesinsrc/tui/screens/system.ts. docs/features/forge-size-use.mddescribes how each figure is measured.
Checks run here, after the merge: bun scripts/gate.ts, bun x tsc --noEmit and bun scripts/design-lint.ts pass. scripts/fast.ts named about 2,000 test files for this diff, so I picked the ones covering it. 259 passed, 0 failed, 8 skipped:
- Before the merge:
spawn*(core, sizes, api, anon, placement, cell),forges-fit, everysystem-*,tui-systemandspawn-self-use: 166 passed, 5 skipped. - After the merge:
spawn-cell,spawn-sizes,spawn-self-use,apps-toolbar,hosted-child-cells,sizes-apiandmetrics-cells: 93 passed, 3 skipped.
Not checked:
- A real cell: "This forge's size" has not been seen running on one. This sandbox can reach only this app's production and previews, and both run as parent forges. That section is covered only by tests:
selfUseagainst a fake cgroup tree, andGET /systemwithFORGE_CELL_SIZEset. - Skipped suites: the tests that need unix sockets or real cells are skipped in this sandbox; the forge's check runs them on this push.
To try it, open /system as the owner of a forge that has made children: each child is listed with its size and use. Open it as a person who is not an admin and neither section appears.
🤖 Generated with Claude Code
Read every changed line. Security: both sections are gated by canSeeMemory (tested with a non-admin GET /system), the cgroup path comes only from the parent-set environment and is only read, links are limited to http(s) and escaped by the html template, no secret, no shell or SQL. Both doors present (web sections plus the System command in the terminal, admin-only), and the how-to is in docs/features/forge-size-use.md with README untouched. Tests check the figures by hand calculation and fail without the change. Design: rows, meter and trail components are used, one footnote sentence, no new accent. parseLedger is now the one validation for both readers. Caveats, not blockers: the preview answered me with a sign-in redirect, so I could not view the owner page there. The child-side block is covered only by tests and not seen on a real cell. This is feature work while the direction puts the pipeline first.
A child forge running in a cell now shows its own size and what it is using, and the parent forge's
/systempage lists each child's use against its size. The terminal's System command shows the same. The new tests and the existing System, spawn and forges suites pass, as do the type check,scripts/gate.tsandscripts/design-lint.ts.cellEnv(src/spawn/cell.ts) now puts the child's size and its cgroup folder into the child's environment, asFORGE_CELL_SIZEandFORGE_CELL_CGROUP_DIR. To get the folder, a cell now exposes it ascgroupDir(src/apps/contain.ts) when the kernel enforces the cell's limits.selfUse(src/spawn/usage.ts):- Memory comes from
memory.current. - CPU is the change incpu.stat'susage_usecdivided by the time between two readings at least a second apart; the first reading is an average since the process started. - Disk reuses the existing background walk of the data folder, kept for 30 s. - If the cgroup can't be read, memory and CPU fall back to the forge's own process only, and the page and terminal say so. - A forge that isn't a cell child shows nothing new.childrenUselists each finished child with its size and its use, read the same way as/forges. A child sharing the parent's process reads "shared with this forge"; a stopped cell reads "not running"./systemgets two sections, "This forge's size" and "Child forges". The terminal getsselfLinesandchildLines, which wrap whole figures rather than cutting off the usage.To keep the terminal command drawing straight away, as the existing System-command test expects, I added
listChildrenNowinsrc/actions/spawn.ts: it reads the list of children without waiting. It shares one ledger-to-child mapping withlistChildren. The last CPU reading is kept in process memory, and that is noted in AGENTS.md as the repo asks. How-to is indocs/features/forge-size-use.md.Not done here: the figures are read when the page loads and are not part of the live 2-second updates, and there is no history chart. I couldn't update the plan on the issue because no
keep_plantool was available in this run.Tests are in
test/spawn-self-use.test.ts. They check the figures against hand calculations (0.5 cores, then 1.5 cores; 1.5 GiB of 4 GiB; 512 MiB of 10 GiB) and the process-only fallback. They also check both sides through a real/systemrequest and the terminal System command. The forge's script that picks tests covering a change named almost the whole suite, so I chose and ran thespawn*,client*,forges*,system-*,tui-systemandsystem-telemetryfiles myself. Tests for unix sockets and real cells are skipped in this sandbox; the forge's check runs them.To try it: as the owner, open
/systemon a forge that has made children to see "Child forges". On a child running in a cell, the same page shows "This forge's size".🤖 Generated with Claude Code
Closes #10853