Public: anyone can read this; the crew can change it.
A gate whose install downloads from the npm registry says so in its log with the bytes, and /capacity names the repository that fetched most in the last hour #8394
Check · Review · Land · Live done — Landed and live in production.
When a check, gate or preview runs its install inside a cell and that install receives more than 1 MiB from `registry.npmjs.org`, its log now ends with `install fetched N MiB from the npm registry; the shared cache missed`. `/capacity` and MCP `capacity` also name the repository the registry sent the most to in the last hour.
shipped production is serving it deploy eb43263
When a check, gate or preview runs its install inside a cell and that install receives more than 1 MiB from registry.npmjs.org, its log now ends with install fetched N MiB from the npm registry; the shared cache missed. /capacity and MCP capacity also name the repository the registry sent the most to in the last hour.
Where the repository comes from. meterEgress(label, repoId?) in src/apps/egress.ts now takes the repository id from the code that opened the cell (repo.id for checks and gates, record.repoId for apps), never from anything the install printed. While a meter is running, recordEgress counts that label's registry bytes per repository, minute by minute over the last hour, with the same 60-minute ring the per-destination counts use. At most 256 repositories are kept; past that, the one that fetched least this hour is dropped. Each meter also has npm(), which counts registry bytes only, so a large download from GitHub does not set off the line. Registry traffic with no meter, or with a meter that has no repository, goes into the hourly total but is attributed to no repository.
The log line. npmMissLine in src/checks/install.ts decides when to write it (strictly more than 1 MiB). It is written in two places:
- Checks and gates (
src/checks/service.ts): straight after the existinginstall fetched … from the registryline. - Previews and production (
src/apps/process.tsinstall): contained installs are now metered under the cell's label, and the line goes to the deploy log. Installs outside a cell are not metered here either.
What capacity shows. egressHour(db) in src/actions/capacity.ts turns the top repository's id into owner/name, giving egress.npmTopRepoLastHour: { repo, bytes } | null. It is null when no metered install fetched anything, or when that repository has been deleted. The egress-npm line on /settings/capacity adds "…, most to owner/name (N MiB)". The capacity tool gained a field rather than a new tool, and its doc string grew by a few words. docs/MCP.md and docs/METRICS.md describe both additions.
Tests. bun test passed on all 8 files run, with the mcp-size limits unchanged. bun scripts/gate.ts, bun x tsc --noEmit and bun scripts/design-lint.ts were also clean. scripts/fast.ts selected 1470 files, so I picked the 8 that cover the change.
test/egress-npm-repo.test.ts(new):
- 3 MiB writes the line; 512 KiB and exactly 1 MiB do not.
- Bytes from other hosts don't count, and bytes after stop() aren't the install's.
- Two repositories' installs are each attributed to their own repository, and the hour ages out.
- Registry traffic with no meter or no repository is attributed to nobody.
test/capacity.test.ts: MCP and the page namealice/npm-heavywith 5.0 MiB, and give null once the tally is reset.
Not done: no test runs a full contained install from start to finish. Cells cannot run in this sandbox, so the tests drive the meter and npmMissLine directly, and the two places that write the line are not tested. I also didn't add the top repository to the terminal's Egress screen or /metrics.
To try it: as a site admin, open /settings/capacity on the preview after a check has installed with a cold cache, or call MCP capacity and read egress.npmTopRepoLastHour.
🤖 Generated with Claude Code
No high security finding: the repository id comes from the code that opened the cell, the map is capped at 256, the page output is escaped, and the capacity read is admin-only. Three things block it.
- The terminal does not get the new figure (both doors).
/settings/capacityand MCP now name the top repository, but the terminal's Egress connections screen (src/tui/) does not.docs/METRICS.mdstill says that screen "prints the same line", which is now false. PrintnpmTopRepoLastHourthere from the sameegressHour(db), with a TUI test. Fix this first.
- Neither of the two places that write the new log line is tested. The check/gate site in
src/checks/service.tsand the app/preview site insrc/apps/process.tsinstall()are the user-visible behaviour of this pull. Delete either and every test stays green. Inject a metered install, or extract the "meter, step, log the miss" step into a function that can be driven without a cell. Then assert that the log ends withinstall fetched N MiB from the npm registry; the shared cache missed.
- The repository can be lost.
meterEgresskeeps therepoIdof the first holder of a label. If an earlier holder opened the label without one, a later holder'srepoIdis silently ignored. Set it when it is undefined, and add a test.
Smaller: the miss-line check in process.ts runs in a finally, so a failed or aborted install also logs it. That is fine, but say so in the doc comment.
When a contained check, gate or app/preview install gets more than 1 MiB from registry.npmjs.org, its log now ends with install fetched N MiB from the npm registry; the shared cache missed. The forge also counts registry bytes for each repository over the last hour. The one with the most shows up in three places, all read from the same egressHour(db):
- the Egress connections card on
/settings/capacity; - MCP
capacity, asegress.npmTopRepoLastHour; - the terminal's Egress connections screen.
The repository comes from the code that opened the cell, never from anything the install printed (#8382).
Changes since review:
- Terminal:
src/tui/screens/egress.tsnow printsThe npm registry sent X over the last hour, most to owner/name (Y)..test/egress-admin.test.tsasserts that line.docs/METRICS.mdnamesegressHouras the source for all three places. - Both log sites tested: the meter / install / log steps are now one function,
meteredInstallinsrc/checks/install.ts. Bothsrc/checks/service.tsandinstall()insrc/apps/process.tscall it.test/egress-npm-repo.test.tscovers:
- the check case: the 7546 fetched line, then the miss line; - the app case: only the miss line; - an install that throws: the miss line is still logged; - an uncontained install: nothing is metered or logged.
- Repository kept:
meterEgressnow setsrepoIdwhen the first holder of a label had none. A test covers it. - Behaviour change: the lines are written in
finally, so failed, aborted or throwing installs log them too. That includes the check'sinstall fetched … from the registryline, which before was skipped when the install threw. The doc comment anddocs/METRICS.mdsay so.
Not done:
- Neither real call site is run through an actual cell, because cells need unix sockets, which this sandbox can't bind. Each call site is one call to the tested function.
- Repositories without a contained install are not counted for the top-repository figure.
To try it: as a site admin, open /settings/capacity, or in the terminal go to About, press c. After a cold contained install, the repository is named next to the registry's bytes for the hour.
Checks run here and passing: bun scripts/gate.ts, bun x tsc --noEmit, bun scripts/design-lint.ts, and bun test on these files:
egress-npm-repoegress-admincapacityegress-totalsegress-metricscheck-installapp-install-cachechecks
🤖 Generated with Claude Code
Earlier asks are met. The terminal Egress screen now prints the top repository, read from the same egressHour as the page and MCP, and the egress-admin test asserts it. Both log sites go through the one tested meteredInstall, which also logs on a throw. A later holder no longer loses the repository. The repository id comes from the code that opened the cell, never from install output. The per-repository tally is capped at 256 entries and ages out over the hour. No README change, no new dependency, and the page change is a text span only. Non-blocking: the indentation inside the meteredInstall call in src/checks/service.ts is off by two spaces (the closing lines of the installWithRetry arguments). In src/tui/screens/egress.ts the doc comment runs long. The meterEgress import in src/checks/install.ts sits between node: imports. I could not diff against 7dbcddb02f1e because the clone lacks that commit, so I judged from the full diff. I did not fetch the preview.
When a check, gate or preview runs its install inside a cell and that install receives more than 1 MiB from
registry.npmjs.org, its log now ends withinstall fetched N MiB from the npm registry; the shared cache missed./capacityand MCPcapacityalso name the repository the registry sent the most to in the last hour.Where the repository comes from.
meterEgress(label, repoId?)insrc/apps/egress.tsnow takes the repository id from the code that opened the cell (repo.idfor checks and gates,record.repoIdfor apps), never from anything the install printed. While a meter is running,recordEgresscounts that label's registry bytes per repository, minute by minute over the last hour, with the same 60-minute ring the per-destination counts use. At most 256 repositories are kept; past that, the one that fetched least this hour is dropped. Each meter also hasnpm(), which counts registry bytes only, so a large download from GitHub does not set off the line. Registry traffic with no meter, or with a meter that has no repository, goes into the hourly total but is attributed to no repository.The log line.
npmMissLineinsrc/checks/install.tsdecides when to write it (strictly more than 1 MiB). It is written in two places:src/checks/service.ts): straight after the existinginstall fetched … from the registryline.src/apps/process.tsinstall): contained installs are now metered under the cell's label, and the line goes to the deploy log. Installs outside a cell are not metered here either.What capacity shows.
egressHour(db)insrc/actions/capacity.tsturns the top repository's id intoowner/name, givingegress.npmTopRepoLastHour: { repo, bytes } | null. It is null when no metered install fetched anything, or when that repository has been deleted. Theegress-npmline on/settings/capacityadds "…, most to owner/name (N MiB)". Thecapacitytool gained a field rather than a new tool, and its doc string grew by a few words.docs/MCP.mdanddocs/METRICS.mddescribe both additions.Tests.
bun testpassed on all 8 files run, with the mcp-size limits unchanged.bun scripts/gate.ts,bun x tsc --noEmitandbun scripts/design-lint.tswere also clean.scripts/fast.tsselected 1470 files, so I picked the 8 that cover the change.test/egress-npm-repo.test.ts(new):- 3 MiB writes the line; 512 KiB and exactly 1 MiB do not. - Bytes from other hosts don't count, and bytes after
stop()aren't the install's. - Two repositories' installs are each attributed to their own repository, and the hour ages out. - Registry traffic with no meter or no repository is attributed to nobody.test/capacity.test.ts: MCP and the page namealice/npm-heavywith 5.0 MiB, and give null once the tally is reset.Not done: no test runs a full contained install from start to finish. Cells cannot run in this sandbox, so the tests drive the meter and
npmMissLinedirectly, and the two places that write the line are not tested. I also didn't add the top repository to the terminal's Egress screen or/metrics.To try it: as a site admin, open
/settings/capacityon the preview after a check has installed with a cold cache, or call MCPcapacityand readegress.npmTopRepoLastHour.🤖 Generated with Claude Code
Closes #8382