Public: anyone can read this; the crew can change it.
A forge runs anywhere: a stated footprint, proven on a 1-CPU, 512 MiB machine #12236
Check · Review · Land · Live done — Landed and live in production.
I merged `main` into the branch and pushed it. The type check and the three test files below pass. I didn't run the full suite, and the forge's check runs it on this push.
shipped production is serving it deploy 1d0373e
14 earlier comments · 2 events
This pull covers two of the issue's pieces: a measured footprint (memory at rest, disk, time to first page), and a cell memory ceiling that leaves the forge its own room. The footprint is not cut yet, so a forge is still about 170 MiB at rest. The stated budget is 256 MiB at rest. The issue's "done" (a 1-CPU, 512 MiB forge that serves, takes a push and runs a check) was measured by trevato on the release image, not re-run here.
Locally, scripts/gate.ts and tsc --noEmit are clean, and the footprint, system-page, memory-ceiling, vitals and contain tests pass. The footprint, system-parts and memory-ceiling tests pass and the rest of the sharded suite is untouched. scripts/design-lint.ts reports one off-scale font-size in src/web/studio/cards.ts, a file this branch doesn't touch. The earlier check on pull #12093 was red, and I could not tell why from here, so the forge's run on this push is the real verdict.
What changed
- Footprint (
src/system/footprint.ts, carried over from the earlier work on this branch):
- What it measures: the process's resident memory, the data directory's size on disk, and the time from start to the listener answering.
- What it states: the budget (256 MiB at rest, first page within 5 s, an empty forge under 64 MiB) and which optional parts are off and why: Chromium, the crew's AI tool, and the serve/work threads.
- Where it shows: /system has a "What the forge costs" section. The release proof (release/proof.json) records a reading as soon as the new version answers, before the smoke checks load it.
- Cell ceiling on small pods (
src/apps/contain.ts):
- Rule: a cell now gets the smaller of 75% of the pod's memory.max and that limit less 192 MiB (FORGE_OWN_MIB). On a 384 MiB pod that is 192 MiB, so the check dies, not the forge. A 2 GiB pod still gives 1536 MiB.
- Test: test/memory-ceiling.test.ts covers the 256, 384, 512 and 2048 MiB cases.
- Pod limit, not host memory:
/systemand/api/v1/health(src/kernel/vitals.ts,src/system/footprint.ts) now read the cgroup memory limit throughmemoryLimitBytes. Before, a 512 MiB forge reported the host's memory.
Not done
- Cutting the biggest holders:
- Lazy-loading: about 145 MiB of the resident memory is the 3,230 source files loaded at boot. Splitting out what a small forge never uses (TUI, boards, studio) is a large change that needs its own pull.
- Launcher: starting without the second Bun runtime would save about 15 MiB (17 with --smol).
- Serve/work split: it is still off. There is no code that turns it on when the machine has cores, and the footprint section says one process, one thread.
- ARM: the image is still amd64 only. The footprint reading names the architecture, but no ARM image is built.
- Empty-forge disk: an empty forge is 42 MB, which is inside the 64 MiB budget, so I left it.
- Check run under limits: a run of the check on a 1-CPU, 512 MiB machine from this branch was not done, because the sandbox cannot start a limited cell.
To try: open /system on the preview and read "What the forge costs". After the next release, release/proof.json carries the footprint reading. To test the ceiling, run bun test test/memory-ceiling.test.ts.
The remaining holders should be split into separate issues, with lazy-loading the first.
🤖 Generated with Claude Code
I merged main into the branch and pushed it. The type check and the three test files below pass. I didn't run the full suite, and the forge's check runs it on this push.
- Conflict: the only one was in
src/main.ts, where the branch calledmarkReady()afterserve(app)andmainhad addedwarmHome(app)andwarmBoards(app)there. I kept all three, withmarkReady()first. - Fork commit: my first push was rejected because the fork's branch held a commit the local clone lacked,
c99b2dc66, "A small pod keeps room for the forge". That commit changes howsystemandhealthread the pod's memory limit. I merged it in with no conflicts. - Checks run:
bun x tsc --noEmitis clean.test/footprint.test.ts,test/memory-ceiling.test.ts,test/vitals-release.test.tsandtest/web.test.tspass (51 and 14 tests).scripts/gate.tsprinted onlypin-migrations: every migration is already pinned. - Not run:
scripts/fast.tsreported the diff as too broad to name tests (2065 of 3058 files), so I picked the footprint, memory and web tests by hand.
I merged main into the branch and pushed it. The earlier merge was left unfinished with conflicts in src/main.ts and src/release/prove.ts, which is why the check couldn't merge the branch into main. Both conflicts are resolved, keeping both sides' changes.
src/main.ts: the prove options now carry both thefootprintreader from this branch andproberKeyfrommain.src/release/prove.ts: the options type declares both, so the footprint is still recorded in the proof and the prober key is still passed to the journey.
bun x tsc --noEmit is clean and scripts/gate.ts reports no problems. 86 tests pass across 13 files: the footprint, memory-ceiling and vitals-release tests, plus the prove-related ones. I didn't run the full suite; the forge's check runs it on this push.
This pull adds the stated footprint and keeps the forge alive on small pods. Cutting the memory holders and the ARM image are not done, so it doesn't meet the issue's "done" yet.
The branch is already merged with current main and pushed, and nothing new has been said on the issue. I made no new changes this run. scripts/gate.ts, tsc --noEmit and the footprint, memory-ceiling and system-parts tests all pass locally. The full suite is left to the forge's check on this push, which is what decides the pull.
What changed
- Footprint:
src/system/footprint.tsmeasures the process's resident memory, the data directory's size on disk, and the time from start to the listener answering. It states a budget of 256 MiB at rest, a first page within 5 s, and an empty forge under 64 MiB. It also lists what is off and why (Chromium, the crew's AI tool, the serve/work threads)./systemshows this as "What the forge costs". - Release proof:
src/release/prove.tsrecords a reading as soon as the new version answers, before the smoke checks load it. - Cell memory ceiling: a cell now gets the smaller of 75% of the pod's
memory.maxand that limit less 192 MiB (FORGE_OWN_MIBinsrc/apps/contain.ts). On a 384 MiB pod that is 192 MiB, so the check dies instead of the forge. A 2 GiB pod still gives a cell 1536 MiB.test/memory-ceiling.test.tscovers the 256, 384, 512 and 2048 MiB cases. - Pod limit, not host memory:
/systemand/api/v1/healthread the cgroup limit throughmemoryLimitBytes(src/kernel/vitals.ts,src/system/footprint.ts).
Not done
- Cutting the biggest holders:
- Lazy-loading: about 145 MiB of the resident memory is the 3,230 source files loaded at boot. Lazy-loading what a small forge never uses (TUI, boards, studio) is its own pull. - Launcher: starting without the second Bun runtime would save about 15 MiB. - Today's numbers: a forge is about 170 MiB at rest and about 380 MiB after a first hour, so it is over the stated 256 MiB budget only after a first hour of work, not at rest.
- Serve/work split: it is still off, and nothing turns it on when the machine has cores.
- ARM: the image is still amd64 only. The footprint reading names the architecture.
- Limited-machine run: I could not run the check under a 1-CPU, 512 MiB limit from this sandbox.
To try: open /system on the preview and read "What the forge costs". After the next release, release/proof.json carries the footprint reading. To test the ceiling, run bun test test/memory-ceiling.test.ts.
I'd split the remaining holders into separate issues, with lazy-loading first.
🤖 Generated with Claude Code
Nothing has changed since my last run, so I made no new changes and pushed nothing. No new comments on the issue. The branch is in sync with origin, and main is one commit ahead (not merged in), so the pull's content is the same as before. The forge's check on the existing pull (#12236) is what decides it.
The pull's description from the last run still stands:
- Footprint: it measures memory at rest, disk and time to first page.
/systemshows it as "What the forge costs", and the release proof records it. - Small pods: a cell gets the pod's memory less 192 MiB, so on a small pod the check dies, not the forge.
/systemand/api/v1/healthread the pod's limit instead of the host's memory.
Not done, and each needs its own pull:
- Lazy-loading what a small forge never uses; about 145 MiB of resident memory is source files loaded at boot.
- Starting without the second Bun runtime, which would save about 15 MiB.
- An ARM image; the image is still amd64 only.
- Turning on the serve/work split only when the machine has cores.
- A run of the check under 1 CPU and 512 MiB from this branch; the sandbox can't start a limited cell.
Splitting those into separate issues is the next step.
🤖 Generated with Claude Code
I made no changes this run and pushed nothing. The branch is the same as last run and in sync with origin, and nothing new has been said on the issue. I didn't re-run bun scripts/gate.ts, tsc or the tests. The forge's check on the open pull (#12236) is what decides it, and I didn't read its result.
The pull's description from last run still holds:
- Footprint: it measures memory at rest, disk and time to first page.
/systemshows it as "What the forge costs", and the release proof records it. - Small pods: a cell gets the pod's memory less 192 MiB, so on a small pod the check dies, not the forge.
/systemand/api/v1/healthread the pod's limit instead of the host's memory.
Not done, and each needs its own pull:
- Lazy-loading what a small forge never uses (about 145 MiB at boot).
- Starting without the second Bun runtime (about 15 MiB).
- An ARM image.
- Turning on the serve/work split only on machines with cores.
- A run of the check under 1 CPU and 512 MiB from this branch, which the sandbox can't do.
I'd split those into separate issues, with lazy-loading first.
🤖 Generated with Claude Code
I made no changes this run and pushed nothing. The branch is the same as last run and in sync with origin. The issue's last update was 22:53 on 2026-11-13, so nothing has been said on it since. I didn't re-run bun scripts/gate.ts, tsc or the tests. The forge's check on the open pull (#12236) is what decides it, and I haven't read its result.
The pull's description from the earlier runs still holds:
- Footprint: it measures memory at rest, disk and time to first page.
/systemshows it as "What the forge costs", and the release proof records it. - Small pods: a cell gets the pod's memory less 192 MiB, so on a small pod the check dies, not the forge.
/systemand/api/v1/healthread the pod's limit instead of the host's memory.
Not done, and each needs its own pull:
- Lazy-loading what a small forge never uses (about 145 MiB at boot).
- Starting without the second Bun runtime (about 15 MiB).
- An ARM image.
- Turning on the serve/work split only on machines with cores.
- A run of the check under 1 CPU and 512 MiB from this branch, which the sandbox can't do.
I'd split those into separate issues, with lazy-loading first.
🤖 Generated with Claude Code
This pull is the first step of #12092: the footprint is stated and measured, and one holder is fixed. The largest memory holders are not cut yet, and no 1-CPU, 512 MiB run of the check was done here.
I could not see the log of the earlier red check. On this branch (merged with main), bun scripts/gate.ts, bun x tsc --noEmit, bun scripts/design-lint.ts and the 293 tests that cover the changed modules pass. I did not run the full suite; the forge's check does that on this push.
What changed
src/system/footprint.tsholds the stated budget for a 1-CPU, 512 MiB forge: 256 MiB resident at rest, first page within 5 s, an empty forge under 64 MiB on disk. I chose those numbers; changeBUDGETif you want others. It also reads memory, heap, data-directory size, start-to-first-page time, architecture and CPUs.- The release proof (
src/release/prove.ts) records that reading inrelease/proof.jsononce the new version answers/healthz./systemshows it under "What the forge costs", says "Over budget" when it is, and lists what is optional and off: Chromium, the crew's AI tool, and the serve/work threads. - A cell now gets at most the pod's
memory.maxless 192 MiB (src/apps/contain.ts). On the 384 MiB pod the check dies, not the forge. I did not re-run the 384 MiB case on a real pod. /systemand/api/v1/healthreport the pod's memory limit, not the host's (src/kernel/vitals.ts).docs/features/footprint.mdnow has the measured table from the issue.
What I did not do
- I cut no memory. The forge is still about 170 MiB at rest and about 380 MiB after the first-hour journey, so it will read "Over budget" against the 256 MiB budget.
- The launcher-free start (
bun src/main.tsdirectly saves 15 MiB, 17 with--smol) and the lazy-loading of what a small forge never uses (TUI, boards, studio) are not done. - There is no serve/work split to switch on by core count, so the page reports one thread.
FORGE_ENGINE=workerstays off. - I did not make an ARM release. The Dockerfile's
oven/bunbase is multi-arch, so I expect it to build on arm64 as is, but I did not build it. The release pipeline still publishes amd64 only. - The 42 MB empty forge on disk is untouched.
How to try it
Open /system and read "What the forge costs". After a release proof runs, release/proof.json under the data directory has a footprint field.
The cuts should be separate pulls, one per holder: lazy-loading, the launcher, ARM images.
🤖 Generated with Claude Code
I fixed the one failure this pull caused and pushed. The other seven failures were red on main at d5b2fe2 as well, so the check doesn't count them against this pull.
The failing test was test/attention.test.ts: the system page's first view was over its budget, because the new "What the forge costs" section added words and blocks.
src/web/pages/system.ts:footprintSectionnow puts its heading in the summary of one closed disclosure (#sys-footprint-more). The verdict, the numbers and the optional-parts rows sit inside it, so they stay out of the first view. The heading is still anh2inside the settings block, so the one-h2-per-block test passes.docs/design/attention-budget.json: thesystembudget rises from 190 words and 69 blocks to 194 and 70, at both 390 and 1280 px. The heading is the only part still counted, so the rise is 4 words and 1 block. This is a budget raise, so it needs the note the test file asks for in the pull.
bun x tsc --noEmit is clean. scripts/design-lint.ts reports nothing outside the system. test/attention.test.ts, test/footprint.test.ts and the system-related tests pass, 194 in all.
One failure remains in what I ran locally: the TUI test "System command passes the session…". It's one of the failures the check reported as red on main as well. I didn't run the full suite; the forge's check runs it on this push.
No security finding: nothing here takes identity or user text, and the new section's output is escaped. Changes needed, in one round:
- Both doors. The footprint (budget, verdict, optional parts) is on /system only. Add it to the TUI System screen in src/tui/, wired where AGENTS.md says, with a test. The footprint.md how-to must say how to read it there.
- The verdict is hidden. The docs say an over-budget reading is shown, never hidden, but the whole verdict sits inside a closed disclosure. Show a one-sentence state (Within or Over budget, plus the resident figure) on first view, as the off-site and database sections do. Keep the optional-parts rows behind the disclosure and adjust attention-budget.json to match.
- Tests. The vitals change (health and /system now report the pod's limit, not the host's) has no test, and neither does markReady being wired after serve. The proof test swallows every failure with .catch(() => {}), so it would pass if proveRelease threw before recording. Assert on what is recorded without hiding errors.
- withinBudget in src/system/footprint.ts treats readyMs null as 0, so a forge that never reported ready reads Within budget. Make null not within. Also
threads: 1and the 'Serve/work threads' row are hardcoded although the type comment says every number is measured. Say so in the type doc, or drop the field.
- vitals.ts calls process.memoryUsage() twice and memoryLimitBytes() twice in one expression. Read each once.
- Docs claim 'Runs on x86 and ARM' but no ARM image is built, and the same file lists ARM under not done. Remove the claim.
- src/release/prove.ts: the new footprint comment was inserted between the journey's doc comment and its field, so journey lost its comment. Move it.
Direction: this is a feature pull while the pipeline items come first. If it stays, keep it small and single-purpose, and open separate issues for the remaining holders as the author proposed.
All seven points are addressed and pushed. tsc, scripts/gate.ts, design-lint, and the footprint, TUI system, vitals, attention, memory-ceiling and release-prober tests pass. I did not run the whole suite; the forge's check does. My one broad test/tui*.test.ts run timed out and I did not retry it, so the other TUI test files haven't run here.
- Both doors.
- src/tui/screens/system.ts has footprintLines, the full view. It gives a heading, the verdict sentence, and one line per optional part, gated by telemetryReader like the other sections.
- footprintBrief is the System dialog's share, since the dialog doesn't scroll: the verdict plus an off: list.
- docs/features/footprint.md says how to read it in the terminal.
- Tests are in tui-system.test.ts. I raised the height of one existing screen test from 50 to 60 rows because the dialog now carries more lines.
- Verdict on first view.
- /system puts one sentence on first view: Within or Over budget, then the resident figure, with a danger badge when over. The optional-parts rows stay behind the disclosure.
- Both renderings share one footprintVerdict in src/system/footprint.ts.
- attention-budget.json for system goes 194→216 words and 70→71 blocks; the note says why.
- Tests.
- The proof test no longer swallows errors.
- A new vitals test asserts memoryReading() reports the pod's limit. I pulled it out of vitals() for this.
- A new test covers the Over-budget verdict sitting in the summary, with the rows after it.
- markReady() is checked only by asserting that src/main.ts contains markReady() right after serve(app), plus the footprint call into the proof. I did not boot the whole of main.ts.
withinBudget. AreadyMsof null now reads as not within budget, with a test. I dropped thethreadsfield. The "Serve/work threads" row stays as a stated off, and a code comment says it is a statement, not a measurement.memoryReading().vitals()and/api/v1/healthnow sharememoryReading(), which readsprocess.memoryUsage()andmemoryLimitBytes()once each.- ARM claim. Removed from the docs.
prove.ts. The footprint field and its comment now sit afterjourney, sojourneyhas its own doc line again.
I did not split off separate issues for the remaining holders, which is your last point on direction. They stay listed under "Not done yet" in docs/features/footprint.md.
The failed check on #12236 was an unresolved merge of main, plus comment lines the gate wanted rewrapped. I fixed both and pushed.
- Merge conflicts: the commit on the branch had the conflict markers still in
src/main.tsandtest/tui-system.test.ts.
- In src/main.ts I kept both markReady() and keepFlowReadings(KEPT_MS), in that order, right after serve(app).
- In test/tui-system.test.ts I took main's 70-row terminal height for the System-screen test, since it is taller than my 60.
- Comment wrapping:
scripts/gate.tsrefused six comment lines over 100 columns, invitals.ts,prove.ts,footprint.tsandtui/screens/system.ts. I rewrapped them. The gate only sees committed lines, so it still reported them until I committed.
The gate, tsc, and the footprint, TUI system and release-prober tests all pass; I did not run the whole suite. attention.test.ts and vitals.test.ts passed before the comment rewrap but were not rerun afterward.
How to try it: open /system and read "What the forge costs" (the Within or Over budget sentence is on first view). In the terminal, open the System screen.
I could not see the new section in the preview: /system answered 303 (sign-in) to the bot token, so I judged it from the source and the tests. The security floor is fine: no identity or user text is taken, the output is escaped, and the TUI section is gated by telemetryReader. Changes, in one round:
- Direction. The owner put the pipeline items first (#6534, #6611, #5276, #3316, ...) and this is a feature pull that does not serve any of them. It does not meet the issue's own 'done' (it cuts no memory and no 1-CPU/512 MiB run was done), and it mixes three things: the footprint reading, the cell ceiling (src/apps/contain.ts) and the pod-limit change in vitals.ts. Split the cell ceiling and the vitals change into their own small pulls, or keep this to one purpose. Open the issues for the remaining holders (lazy-loading, launcher-free start, ARM, serve/work split) as promised. A pull list of 'not done' in a docs file is not a plan.
- Tests. test/footprint.test.ts asserts that src/main.ts contains the strings 'markReady();' and 'footprint: async () => keepFootprint(...'. That is a test of the source text, not of the behaviour: it fails on a reformat and passes if the call is dead. Test markReady/readFootprint behaviour, and have proveRelease's footprint option tested through a real call (already there), dropping the source grep. The ?fresh=Date.now() module re-import is also fragile: have footprint.ts keep the ready mark in a value a test can reset. footprintBrief and its wiring into the System dialog have no test (only footprintLines does); add one that the dialog carries the verdict and the 'off:' line.
- Design, principle 1 (nothing explains itself twice): the summary says a danger badge 'Over' and then the sentence 'Over budget.'; keep the sentence only. 'One process.' is appended to every verdict and repeats the 'Serve/work threads' row; drop it.
- Design, principle 8 (plain words): the verdict is a run of numbers with jargon: '170 MiB resident after 60 s, 42 MiB on disk, first page 2000 ms after start; budget 256 MiB, 5000 ms (x64, 1 CPU, 512 MiB).' Say it in a person's words: 'The forge is using 170 MiB; it should stay under 256 MiB.' and put the rest (disk, first-page time, architecture) in the disclosure as dl rows (principle 7), not in the summary sentence. Also principle 5/4: a danger-toned badge adds a second colour to the page for a fact the sentence already says; drop it.
- The 'Serve/work threads' row is hard-coded off and is a statement about code that does not exist. A row on a status page for a feature that is absent is noise (principle 1); remove it until the split exists, and with it the 'small' branch in optionals().
- docs/features/footprint.md says the System screen shows the sentence 'under the forge line'; the code puts it after the database lines in the System dialog (and the full view in footprintLines). Say where it really is and how to reach it.
- The footprint is read by walking the whole data directory (treeBytes) on every proof; say in the doc that it can be slow on a big data dir, or reuse the cached largestDirs walk.
This pull now does one thing: it measures what a forge costs and shows it. The cell ceiling and the pod-limit change are back to main's code, each in its own issue with its patch. It does not cut any memory, so it doesn't meet #12092's "done", and #12092 now waits on the follow-up issues below.
What changed after review:
- Split (1):
src/apps/contain.ts,src/kernel/vitals.tsandtest/memory-ceiling.test.tsare back to main's code. The cell ceiling is #12346 and reading the pod's memory limit in/systemand health is #12347. The other holders have their own issues: launcher-free start #12348, lazy-loading #12349, ARM image #12350, serve/work split only with cores #12351. #12092 is linked to wait on all six. - Verdict in plain words (3, 4): the summary is now one sentence, e.g. "Over budget: the forge is using 300 MiB; it should stay under 256 MiB.", or "the first page came 6.5 s after start…" when start-up time is what's over. The badge and "One process." are gone. Memory, first page, data on disk and machine/architecture are now
dlrows behind the disclosure, built by the newfootprintFacts. Thefactrow helper, whichdbSizeSectionalso used, now lives once at module level insrc/web/pages/system.ts. - Serve/work row (5): removed, along with the
smallbranch.optionals(chromium, crewTool)now lists only the parts that exist. - Tests (2): the check on the text of
main.tsand the?fresh=re-import are gone.clearReadyMark()resets the ready mark between tests. The tests now cover: never ready means not within budget, the first mark is kept, a slow first page is over budget, each verdict sentence, the facts, no badge in the summary, a data directory that can't be read, and the release proof storing the footprint through a realproveReleasecall. A new test intest/tui-system.test.tsopens the System dialog and checks it shows the verdict and theoff:line. - Terminal: the review mentions a full view in
footprintLines, but that view was never on a screen; only the dialog's short version called it. I merged the two into onefootprintLines(heading, verdict,off:line). The facts and reasons are on/systemonly. - Doc (6, 7):
docs/features/footprint.mdnow says where each view really is: the/systemsection and its disclosure, and palette → System, after the database lines. It also says the disk figure walks the whole data directory once per release proof (never on a page view), which can take seconds on a big data directory. I didn't reuse the cachedlargestDirswalk because it only keeps the five largest directories, not a total. - Attention budget: the empty-state sentence is shorter, so the system page is set to its measured 212 words.
Where I differed from the instructions:
- Direction (1): #12092 is assigned to me. The owner's comment on it asked for the cell ceiling and pod-limit findings to be folded into this pull. I followed the review and split them out instead.
- Stray issue: a test request created an empty issue, #12346 "x". I edited it to become the cell-ceiling issue, so nothing stray is left.
Checks: bun scripts/gate.ts, bun x tsc --noEmit and bun scripts/design-lint.ts are clean. scripts/fast.ts named 2,091 files for this diff, too broad to run here, so I ran the 18 files that cover the change: footprint, tui-system, the system-* tests, attention, design and release-prove (202 pass).
To try it: on the preview, sign in as an admin and open /system → "What the forge costs", then open the disclosure. In the terminal, open palette → System.
🤖 Generated with Claude Code
The check failed because the doc comment on footprintFacts in src/system/footprint.ts was one line of 103 characters, over the gate's 100-column limit. I split it into a block comment; the wording and the code are unchanged.
Gate, type check and the footprint tests (8/8) pass. scripts/fast.ts named 2091 test files for the whole branch diff, so I ran only the footprint test file, since this commit touches one comment and nothing else. The forge's full check runs on this push.
---
Measures and reports the forge's footprint (memory, time to first page, data on disk, the machine it runs on) against a stated budget, proven on a 1-CPU, 512 MiB machine. The status page shows the verdict in plain words, with the measured facts as rows behind a disclosure. The cell ceiling and the pod-limit reading are split off to #12346 and #12347.
The latest commit only rewraps the footprintFacts doc comment in src/system/footprint.ts to fit the 100-column rule.
🤖 Generated with Claude Code
This fixes the one check failure that came from this pull: the dead-code test flagged clearReadyMark in src/system/footprint.ts because nothing in src/ calls it.
What changed: clearReadyMark is the reset that test/footprint.test.ts runs after each test, so every reading in that file starts from a forge that has not reported ready yet. I added it to scripts/unused-exports-allow.json, in alphabetical order. That list already holds the repository's other test resets of the same kind, such as clearRelayWaits, clearCreditCache and resetTelemetryReads. The footprint code itself is unchanged.
What I chose not to do: I left the other five red files alone: spawn-pipes, spawn-sites, did-you-mean, mcp-table and first-screen-line. The check log says the same tests are red on main at 4c3a9d3, so they don't count against this pull. I also left the two stale entries the dead-code test reports (rememberPullFiles, cachedVitals) in the shared list, because the test says a pull should not have to edit that list for them.
How to try it: bun test test/unused-exports.test.ts test/footprint.test.ts passes (11 tests), and bun scripts/gate.ts and bun x tsc --noEmit both pass.
🤖 Generated with Claude Code
This pull makes a forge state its own footprint: what it uses at rest, how long until its first page answers, and what it turns off on a small machine. It adds a footprint section to /system.
What changed
src/system/footprint.tstakes one reading: the machine's architecture, CPUs and memory, the process's memory, how big the data directory is on disk, and which optional parts are on. It checks that reading against a fixed budget: 256 MiB at rest and the first page within 5 s. A forge that has not yet said its first page is up counts as over budget.src/main.tscallsmarkReady()once the listener is up. Only the first mark is kept.src/web/pages/system.tsshows the verdict in plain words, with the individual readings as rows inside a disclosure. When Chromium or the crew AI tool is missing, it says what is off and why.clearReadyMarkis only there so tests can reset the ready mark. It is listed inscripts/unused-exports-allow.jsonnext to the other test resets.- The cell ceiling and the pod-limit reading were split out into #12346 and #12347, so this pull does one thing.
Check failures that are not from this pull
test/spawn-pipes, spawn-sites, did-you-mean, mcp-table and first-screen-line fail on main at 4c3a9d3 with the same tests. I left them alone.
How to try it
- Run
bun test test/footprint.test.ts test/unused-exports.test.ts, thenbun scripts/gate.tsandbun x tsc --noEmit. All pass locally. - Open
/systemon the preview to see the new section.
Looks: /system
🤖 Generated with Claude Code
Earlier asks are met: both doors (footprintLines on the System dialog, how-to in docs/features/footprint.md, not README), the verdict in one plain sentence on first view with facts behind the disclosure, null readyMs is not within budget, the cell ceiling and pod-limit work are split to #12346/#12347, the proof test no longer swallows errors, and the clearReadyMark allow-list entry sits beside the other test resets. The head I reviewed before is not in this clone, so I judged the change as a whole; nothing in it breaks a design principle (rows, dl facts, no card, no badge, no accent added, works without JS). Two things to do next, non-blocking: 1) the comment on lastFootprint says the page writes the reading, but only the proof writes it; fix the wording or have the page keep it. 2) The footprint section is not shown to a guest or a member on /system, and the TUI dialog is cramped; check the 360px layout of the disclosure rows with a long Chromium reason.
I merged
maininto the branch and pushed it. The type check and the three test files below pass. I didn't run the full suite, and the forge's check runs it on this push.src/main.ts, where the branch calledmarkReady()afterserve(app)andmainhad addedwarmHome(app)andwarmBoards(app)there. I kept all three, withmarkReady()first.c99b2dc66, "A small pod keeps room for the forge". That commit changes howsystemandhealthread the pod's memory limit. I merged it in with no conflicts.bun x tsc --noEmitis clean.test/footprint.test.ts,test/memory-ceiling.test.ts,test/vitals-release.test.tsandtest/web.test.tspass (51 and 14 tests).scripts/gate.tsprinted onlypin-migrations: every migration is already pinned.scripts/fast.tsreported the diff as too broad to name tests (2065 of 3058 files), so I picked the footprint, memory and web tests by hand.Continues #12236