OpenCode / Memory lab

Shell jobs · 32 KiB output

Run completed shell jobs with 32768-byte outputs, using PID-tagged payloads to identify them in private heap snapshots.

Inconclusive

Cycles / repetition

100

30 warm-up excluded

Heap change

+7.7 MiB

Median case, end − baseline

Adjusted growth

+48668.6 B/cycle

Positive control drift removed

Repetitions

1 / 1

Fresh case + control processes

Post-GC heap

CaseControlWarm-up
122.0130.2138.4146.5154.70336598130MiBcontrol-1 · 0 cycles · warmup · 150.76 MiBcontrol-1 · 30 cycles · baseline · 125.96 MiBcontrol-1 · 50 cycles · measurement · 126.16 MiBcontrol-1 · 70 cycles · measurement · 126.23 MiBcontrol-1 · 90 cycles · measurement · 126.30 MiBcontrol-1 · 110 cycles · measurement · 125.99 MiBcontrol-1 · 130 cycles · measurement · 126.57 MiBcase-1 · 0 cycles · warmup · 150.74 MiBcase-1 · 30 cycles · baseline · 127.95 MiBcase-1 · 50 cycles · measurement · 131.93 MiBcase-1 · 70 cycles · measurement · 133.36 MiBcase-1 · 90 cycles · measurement · 134.27 MiBcase-1 · 110 cycles · measurement · 135.03 MiBcase-1 · 130 cycles · measurement · 135.61 MiB

Completed cycles

20 cycles per batch · 3 full GC requests per checkpoint · All measured points shown · Y-axis zoomed

Inconclusive. The run is incomplete, too short, or does not show a consistent settled trend. Do not interpret this as a pass.

Snapshot finding: completed Job records persist. After idle GC, all 132 completed jobs still have closed scopes and fixture-tagged output nodes. Each command emitted 32 KiB. The separate shell cache holds 25 entries and no longer contains the oldest job. This structural result is separate from the memory-trend verdict above.

Idle GC & heap evidence

Raw snapshots are private and are not served by this website. Only filtered structural findings are published.

TrialIdle secondsAfter work MiBAfter idle GC MiBSession messages
control-115126.57125.480
case-115135.61131.840

Raw idle observations. These observations are separate from the slope fit; a lower heap after idle does not by itself establish that completed outputs were released.

Records are matched by the Core Job.Active reference fields and fixture session, not by class name alone. Fixture marker and child-PID prefixes identify their output nodes. The two control jobs are the real preflight commands.

Trial / snapshotExecutedJob recordsClosed scopesTagged outputsShell cache
case-1 / baseline3232323225
case-1 / final13213213213225
case-1 / idle13213213213225
control-1 / baseline22222
control-1 / final22222
control-1 / idle22222

Snapshot limitation. Bun exports these job objects without incoming edges, so no complete GC-root retaining path is available. Missing paths do not establish collectibility. A two-process calibration using the same embedded Bun reproduces this for a live Map-only value, while a direct object property has an incoming edge.

The actual record → info → output reference edges are preserved in the filtered data. Bun truncates string labels to 1,024 characters, so the snapshot identifies output prefixes rather than verifying all payload bytes. Exact output size was checked during execution. Output-node shallow bytes are not total retained size.

Filtered object counts and reference edges

Verified behavior & retention hypothesis

The process-global Job service stores completed jobs, their deferred results, metadata, and output in its Map. The shipped shell/subagent executors create jobs, but Job has no removal or bounded-exit cache, unlike Shell's 25-entry cache.

control-1
Functional preflight passed (2 case + 2 control cycles).
{"completed":2,"controlCompleted":132,"progressCalls":2,"outputBytes":65536,"bytesPerCommand":32768,"sessionMessages":0,"sessionID":"ses_f9bdd9fb7ffeLoIky5kCuYjEER","firstShellID":"sh_064226076001gopBdIh0I0Vnd2","lastShellID":"sh_0642260bc003ei8m2kfelSXjRi","outputMarker":"JOBMEM_LARGE:"}
case-1
Functional preflight passed (2 case + 2 control cycles).
{"completed":132,"controlCompleted":2,"progressCalls":132,"outputBytes":4325376,"bytesPerCommand":32768,"sessionMessages":0,"sessionID":"ses_f9bdcd07affeQYoLv2UZAJ0Nr6","firstShellID":"sh_064232fc5001KpAAbslOM7SXvF","lastShellID":"sh_064239bb50015tSUYQSv54kWKN","outputMarker":"JOBMEM_LARGE:"}

Control cadence is calibrated from two real preflight cycles in each process and recorded as controlCadenceMs. Both arms get at least 20 seconds of warm-up/settling before the baseline. This approximately matches elapsed time, not every internal allocation or polling read.

Source inspected at 4772b6a3e8; this is the investigation reference, not a claim about the binary’s build commit.

Method & run details

One fixed session and one inspector transform remain. Commands finish before measurement; the fixture retains neither results nor a growing ID list. Session context remains empty.

Control. Run the same executable and output through a plain subprocess, without OpenCode's shell/job bookkeeping. Both arms are cadence-matched and warmed beyond the shell service's 25-exit cache.

Metric: target process.memoryUsage().heapUsed after Bun.gc(true). Warm-up excluded. Theil–Sen slope; later-half slope must remain positive. Subtract positive control drift only. A growing control, large baseline drift, or a noise allowance above max(1 MiB, 5% of baseline) that prevents detecting a trend makes attribution inconclusive. Net growth and slope × cycles must both exceed max(128 KiB, 6 × combined robust residual noise). Require agreement across at least two fresh-process repetitions. Heuristic, not a significance test.

OpenCode
0.0.0-dev-18911 · dev channel · installed binary
Drive
1.4.4 · installed package
Runtime
Bun 1.4.0 · linux / x64
Isolation
Private server + file-backed database. TUI closed before warm-up. Measurement plugin remains loaded; no object-level instrumentation.
Checkpoints
Case-specific cleanup checks pass before GC; 300 ms settling allowance. This is not a global background-work barrier.
Snapshots
Baseline and final, plus post-idle snapshots enabled. Raw heaps are private under the run's .cache directory, not served by this website.
Run
2026-09-02T21-59-26-177Z-95f1bd · 2026-09-02T21:59:26.177Z
cd /root/projects/opencode-memory-lab && bun run run --case shell-jobs-large --warmup 30 --batches 5 --batch-size 20 --repetitions 1 --settle-ms 300 --warmup-ms 20000 --idle-ms 15000 --snapshots
RepetitionCase Δ MiBControl Δ MiBAdjusted B/cycleGate KiB
17.660.6148668.63440
Process RSS

Includes native allocations and allocator behavior. Not used to classify JS heap growth.

244.5258.9273.4287.9302.30336598130MiBcontrol-1 · 0 cycles · warmup · 273.25 MiBcontrol-1 · 30 cycles · baseline · 251.50 MiBcontrol-1 · 50 cycles · measurement · 251.91 MiBcontrol-1 · 70 cycles · measurement · 262.63 MiBcontrol-1 · 90 cycles · measurement · 263.91 MiBcontrol-1 · 110 cycles · measurement · 262.29 MiBcontrol-1 · 130 cycles · measurement · 252.70 MiBcase-1 · 0 cycles · warmup · 270.91 MiBcase-1 · 30 cycles · baseline · 272.41 MiBcase-1 · 50 cycles · measurement · 268.93 MiBcase-1 · 70 cycles · measurement · 270.62 MiBcase-1 · 90 cycles · measurement · 273.22 MiBcase-1 · 110 cycles · measurement · 276.85 MiBcase-1 · 130 cycles · measurement · 295.33 MiB
Measurements · 14 checkpoints
TrialPhaseCyclesHeap MiBRSS MiBPIDGC ms
control-1warmup0150.76273.25409296.6
control-1baseline30125.96251.504092174.4
control-1measurement50126.16251.914092172.8
control-1measurement70126.23262.634092173.2
control-1measurement90126.30263.914092218.2
control-1measurement110125.99262.294092204.7
control-1measurement130126.57252.704092175.5
case-1warmup0150.74270.915035104.3
case-1baseline30127.95272.415035226.8
case-1measurement50131.93268.935035195.3
case-1measurement70133.36270.625035224.9
case-1measurement90134.27273.225035233.4
case-1measurement110135.03276.855035212.1
case-1measurement130135.61295.335035209.1