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

Merged opened this

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

Merged

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

opened this

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, as FORGE_CELL_SIZE and FORGE_CELL_CGROUP_DIR. To get the folder, a cell now exposes it as cgroupDir (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: childrenUse lists 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: /system gets two sections, "This forge's size" and "Child forges". The terminal gets selfLines and childLines, 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

Closes #10853

commented

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, as FORGE_CELL_SIZE and FORGE_CELL_CGROUP_DIR. To get the folder, a cell now exposes it as cgroupDir (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: childrenUse lists 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: /system gets two sections, "This forge's size" and "Child forges". The terminal gets selfLines and childLines, 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

commented

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.ts reads them, from the kernel's cgroup files when the cell has a cgroup. To make that possible, src/apps/contain.ts now records the cell's cgroup directory (cgroupDir). src/spawn/cell.ts and src/actions/spawn.ts pass that through to the pages.
  • Pages: the new sections are in src/web/pages/system.ts and src/tui/screens/system.ts.
  • Tests: test/spawn-self-use.test.ts covers 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 requested — before the latest push

Changes needed (round 1, all in one list):

  1. 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.
  1. 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.stat and '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.
  1. 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.
  1. 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.
  1. 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.
  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.

commented

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:

  1. 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.
  2. 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.
  3. 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. ChildUseRow now carries url for the link.
  4. One ledger check: a new parseLedger in src/actions/spawn.ts accepts a ledger only when it is JSON with a string name, a string bodyId and a boolean done. readLedger, and so listChildren, uses it, and so does the terminal's listChildrenNow. A test puts an invalid bad.json next to a good ledger and checks that both readers skip it.
  5. 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 on GET /system.
  6. 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: selfUse against a fake cgroup tree, and GET /system with FORGE_CELL_SIZE set. 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

commented

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 /system sees neither.
  • One ledger check: a new parseLedger in src/actions/spawn.ts is the validation both the page's listChildren and the terminal's listChildrenNow use. A file that is not JSON, or lacks name, bodyId or done, is skipped the same way by both.
  • Merge with main: the only conflict was the imports in src/spawn/cell.ts. It now imports both SIZE_IO (the disk-bandwidth cap that landed on main in #10867) and this pull's CGROUP_DIR_ENV/SIZE_ENV.

How the code is laid out:

  • The reading itself is in src/spawn/usage.ts.
  • The page sections are selfSection and childrenSection in src/web/pages/system.ts.
  • The terminal lines are selfLines and childLines in src/tui/screens/system.ts.
  • docs/features/forge-size-use.md describes 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, every system-*, tui-system and spawn-self-use: 166 passed, 5 skipped.
  • After the merge: spawn-cell, spawn-sizes, spawn-self-use, apps-toolbar, hosted-child-cells, sizes-api and metrics-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: selfUse against a fake cgroup tree, and GET /system with FORGE_CELL_SIZE set.
  • 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

Approved

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.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs