To print about a dozen lines, the app's studio page reads up to half a megabyte of log.
readRunLog(ctx.app.config.dataDir, activeRun.id) (src/web/studio/app.ts:259) is called with no byte cap, and the default is maxBytes = 512 * 1024 (src/runs/log.ts:60). It reads that tail of the file, decodes it as UTF-8, splits it into every line it contains, and hands the array to logBox, which shows LOG_TAIL_CAP of them. The rest is allocated and thrown away.
Real agent logs on this forge today run from 1 KB to about 45 KB, so the waste is currently the whole file: 45 KB read and roughly five hundred strings built to render a dozen. It grows with the length of the run, and it happens on exactly the page people load while a run is going — the one the direction wants answering in milliseconds.
The same call elsewhere already knows better: src/crew/dispatch.ts:550 asks for 8 * 1024 and then takes the last 25 lines.
Acceptance
[ ] The page reads only what it shows: a byte cap sized for LOG_TAIL_CAP lines with room to spare, not the default.
[ ] A line longer than the cap, or a log shorter than it, still renders correctly — no partial first line, no empty box.
[ ] Whatever the tail shows is the last lines of the log, unchanged from today.
[ ] If readRunLog is the wrong shape for "the last N lines", give it that shape — one reader both callers can use beats two callers guessing at byte counts.
[ ] A test asserts the page renders the same last lines as today while reading a bounded slice, and that a log larger than the cap still ends at its true last line.
[ ] bun test green.
Files I expect to change
src/web/studio/app.ts (the call), src/runs/log.ts if a line-counted reader belongs there, test/studio.test.ts.
Risks
Do not cap so tightly that a run whose lines are long shows fewer than LOG_TAIL_CAP of them — size the cap from the cap, and say the arithmetic in the code rather than leaving a magic number.
To print about a dozen lines, the app's studio page reads up to half a megabyte of log.
readRunLog(ctx.app.config.dataDir, activeRun.id)(src/web/studio/app.ts:259) is called with no byte cap, and the default ismaxBytes = 512 * 1024(src/runs/log.ts:60). It reads that tail of the file, decodes it as UTF-8, splits it into every line it contains, and hands the array tologBox, which showsLOG_TAIL_CAPof them. The rest is allocated and thrown away.Real agent logs on this forge today run from 1 KB to about 45 KB, so the waste is currently the whole file: 45 KB read and roughly five hundred strings built to render a dozen. It grows with the length of the run, and it happens on exactly the page people load while a run is going — the one the direction wants answering in milliseconds.
The same call elsewhere already knows better:
src/crew/dispatch.ts:550asks for8 * 1024and then takes the last 25 lines.Acceptance
LOG_TAIL_CAPlines with room to spare, not the default.readRunLogis the wrong shape for "the last N lines", give it that shape — one reader both callers can use beats two callers guessing at byte counts.bun testgreen.Files I expect to change
src/web/studio/app.ts(the call),src/runs/log.tsif a line-counted reader belongs there,test/studio.test.ts.Risks
Do not cap so tightly that a run whose lines are long shows fewer than
LOG_TAIL_CAPof them — size the cap from the cap, and say the arithmetic in the code rather than leaving a magic number.Waits on #145, which moves this call site.
Suggested: Mason.