feat(bench): embedded-RSS benchmark harness + report-only CI (#5046)#5060
Conversation
…inyhumansai#5046) Add the reproducible steady-state RSS benchmark that the 20-30 MiB embedded budget is measured against, plus a report-only CI lane. - `openhuman::proc_metrics` (ungated): Linux /proc/self/{status,smaps_rollup} sampling + roster aggregation + serde report + human summary. OS-agnostic parsers are unit-tested; `sample_self()` is Linux-only and errors elsewhere rather than fabricating a reading. - `rss-bench` binary (gated behind the default-OFF `rss-bench` feature): mirrors the OpenCompany embedding contract — a bare `Agent` via `Agent::builder()` (no CoreBuilder / RPC / background services), injected mock provider + `"none"` memory + per-agent temp workspace, 1- and 8-agent rosters, one warm-up turn + settle, sampled across 5 fresh child processes per roster. - Report-only `rust-rss-bench` CI job: uploads raw samples + a human summary; deliberately not in `pr-ci-gate.needs`, so it can never fail a PR until the 30 MiB gate is flipped to blocking in a follow-up. The default/shipped build is byte-identical (feature is default-OFF, no new deps).
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds Linux process-memory metrics, a feature-gated Rust RSS benchmark for isolated agent rosters, JSON and Markdown reporting, unit tests, and a report-only CI job triggered by ChangesRSS benchmark
Estimated code review effort: 4 (Complex) | ~45 minutes Possibly related issues
Suggested labels: Poem
Sequence Diagram(s)sequenceDiagram
participant CI as CI Lite
participant Parent as rss-bench parent
participant Child as rss-bench child
participant Metrics as proc_metrics
CI->>Parent: Run benchmark
Parent->>Child: Execute each roster size and repeat
Child->>Metrics: Sample settled process RSS
Metrics-->>Child: Return ProcSample JSON
Child-->>Parent: Return child output
Parent->>Parent: Aggregate BenchReport
Parent-->>CI: Write summary and bench-rss.json
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Comment |
…inyhumansai#5046) Add BenchReport::per_agent_increment_kib(), which derives the marginal steady-state RSS cost of each additional agent from the smallest and largest rosters — (median_rss(max) - median_rss(min)) / (max_size - min_size). For the default {1, 8} rosters this is the per-agent cost of agents 2-8, which the human summary now prints. Returns None when fewer than two distinct roster sizes were measured. Two unit tests cover the computed value and the single-roster case. Addresses the issue's "bounded per-agent growth" criterion (the report now reports the incremental cost, not just the two roster totals).
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bdf02f7b70
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/bin/rss_bench.rs`:
- Around line 302-310: Add stderr-only debug/trace logging across
src/bin/rss_bench.rs, including main flow, roster construction, warm-up,
settling, child spawn and completion, report writes, and failures; keep child
stdout limited to its single JSON sample. In src/openhuman/proc_metrics/mod.rs,
trace sampling entry and exit plus read failures without including sensitive
data.
- Around line 203-209: Update the child-process execution in the benchmark flow
around tokio::process::Command to add a per-child timeout. Log child spawn,
timeout, kill, and exit events at debug level; on timeout, kill the child
process and await it afterward to reap it, while preserving contextual errors
for spawn and normal output handling.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: e60d26e1-cd86-4a44-be72-5348ce8b94e5
📒 Files selected for processing (5)
.github/workflows/ci-lite.ymlCargo.tomlsrc/bin/rss_bench.rssrc/openhuman/mod.rssrc/openhuman/proc_metrics/mod.rs
tinyhumansai#5046) From CodeRabbit review: - Give each benchmark child a 120s wall-clock budget: spawn with kill_on_drop and wrap wait_with_output in a timeout, so a stalled child is killed and reaped rather than blocking the whole run until the outer CI job timeout. - Add stderr-only diagnostics for the benchmark flow (child spawn/timeout/exit, and roster build/warm-up/settle/sample in the child) so a run is observable; kept on stderr so they never corrupt the single JSON line the parent parses from stdout.
…lake Under cfg(test) the crate's ~12.6k unit tests share two process-global bounded resources sized for production: the event bus's 256-slot broadcast channel (src/core/event_bus/bus.rs) and the learning candidate ring's 1024 entries (src/openhuman/learning/candidate.rs, whose drainer does not run in the unit suite). Under the concurrent full-suite publish load a slow subscriber lags and drops events (RecvError::Lagged), and the candidate ring evicts a test's own candidates — intermittently failing bus-delivery tests such as learning::startup::tests::learning_subscriber_fires_with_no_channel_configured. Raise both floors to 65536 for the TEST BINARY ONLY (cfg(test)); production keeps 256 / 1024 and ships byte-identical. No new dependency, no serialization, no weakened assertion — the shared globals simply get enough headroom that neither drops nor evicts under the artificial suite-wide load.
…ated (tinyhumansai#5046) Codex review: `create_memory(MemoryConfig{ backend: "none", .. })` does not select a no-op backend — `create_memory_full` always builds a `UnifiedMemory` (SQLite + the default cloud embedder) regardless of `backend`. So every measured agent was allocating a memory store + embedder, inflating the 1/8-agent RSS baseline with memory-store setup rather than measuring the bare agent under the OpenCompany embedding contract (host supplies its own Memory). Replace the fixture's `create_memory` call with a zero-allocation `NoopMemory` that implements the `Memory` trait as inert (store discards, recall/list/get return empty, count 0). This mirrors the host-supplied-memory contract and keeps the reading on the agent harness itself.
There was a problem hiding this comment.
🧹 Nitpick comments (1)
src/bin/rss_bench.rs (1)
266-338: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd coverage for the parent/child report protocol.
The included tests only exercise roster construction and warm-up; they do not cover valid/invalid child JSON, empty stdout, non-zero exits, or report serialization. Extract the child-output parsing into a unit-testable helper and cover those cases.
As per coding guidelines, “New or changed flows must include … unit-test it in Rust.”
Also applies to: 404-436
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/bin/rss_bench.rs` around lines 266 - 338, The run_parent child-output flow lacks unit-testable coverage for protocol failures and report serialization. Extract parsing of child status, stdout, and JSON into a dedicated helper near run_parent, preserving contextual errors for invalid JSON, empty stdout, and non-zero exits; then add Rust unit tests covering valid JSON, invalid/empty output, failed status, and BenchReport serialization.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@src/bin/rss_bench.rs`:
- Around line 266-338: The run_parent child-output flow lacks unit-testable
coverage for protocol failures and report serialization. Extract parsing of
child status, stdout, and JSON into a dedicated helper near run_parent,
preserving contextual errors for invalid JSON, empty stdout, and non-zero exits;
then add Rust unit tests covering valid JSON, invalid/empty output, failed
status, and BenchReport serialization.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 2085c096-acfd-4f76-b406-d7332e4edc9a
📒 Files selected for processing (3)
src/bin/rss_bench.rssrc/core/event_bus/bus.rssrc/openhuman/learning/candidate.rs
📌 Note for reviewers — the event-bus change here is an unrelated, self-healing flake fixThis PR carries a small test-only change that is not part of its objective:
They fix two pre-existing full-suite flakes — Root cause (corrected): not a capacity problem. Both tests asserted against the process-global broadcast bus, so under the ~12,600-test parallel suite they received events published by other concurrent tests — cross-test interference, which no channel-capacity bump can fix. (An earlier capacity-floor attempt was pushed and then reverted here once CI proved it ineffective.) Fix — real isolation:
Production ships byte-identical — the diff is confined to Why it's in this PR: this PR changes Pre-existing CI red unrelated to this PR (not merge-blocking — not in
|
…city floor The earlier test-only capacity floor (5c49a96) did not fix the flake: the two failing tests share the process-global broadcast bus, so under the parallel suite they receive events published by *other* tests — a capacity bump cannot fix cross-test interference. Replace it with real isolation. - learning::startup: rewrite `learning_subscriber_fires_*` to publish a DocumentCanonicalized event on a private `EventBus::create(64)` with a locally-subscribed `EmailSignatureSubscriber`, instead of asserting on the shared global bus. Add a synchronous `register_email_signature_subscriber` guard test for the channel-independent registration path (tinyhumansai#5003). - web_chat::event_bus: the automation-halt test reads from the shared web-channel bus; drain foreign events and match on `event == "automation_halt"` rather than taking whatever is first in the buffer. - Revert the `#[cfg(test)]` capacity floors in event_bus/bus.rs and learning/candidate.rs back to origin/main — no longer needed.
|
Review — looks solid overall. The CI — both red lanes are unrelated to this PR (branch is behind
Both are resolved by merging/rebasing latest Minor nits (non-blocking):
|
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
src/openhuman/learning/startup.rs (1)
273-325: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick winAdd grep-friendly diagnostics to the new event-isolation paths.
These tests specifically address shared-bus flakes, but failures currently omit the test marker, subscription/publish state, expected versus observed count, and lag/foreign-event details.
src/openhuman/learning/startup.rs#L273-L325: trace the unique source ID, isolated subscription/publish, expected candidate count, and final observed count.src/openhuman/web_chat/event_bus.rs#L512-L525: trace skipped foreign event names, lag occurrences, and the matched test-owned event marker; do not log payload PII.As per coding guidelines, “New or changed flows must include verbose, grep-friendly diagnostics covering entry/exit, branches, external calls, retries/timeouts, state transitions, and errors.”
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/openhuman/learning/startup.rs` around lines 273 - 325, The isolated learning startup test around learning_subscriber_fires_on_document_event must add grep-friendly tracing for the unique source ID, isolated subscription and publish steps, expected candidate count, and final observed count. In src/openhuman/web_chat/event_bus.rs lines 512-525, add diagnostics for skipped foreign event names, lag occurrences, and the matched test-owned event marker without logging payload PII; ensure both sites cover the requested state transitions and error paths.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/openhuman/web_chat/event_bus.rs`:
- Around line 512-525: Update next_automation_halt to match the test-owned
automation_halt event using a unique source marker, not just ev.event. Also
require the expected engaged value and, for the halted case, the expected
reason; continue draining unrelated events and preserve the existing empty and
closed error handling.
---
Outside diff comments:
In `@src/openhuman/learning/startup.rs`:
- Around line 273-325: The isolated learning startup test around
learning_subscriber_fires_on_document_event must add grep-friendly tracing for
the unique source ID, isolated subscription and publish steps, expected
candidate count, and final observed count. In
src/openhuman/web_chat/event_bus.rs lines 512-525, add diagnostics for skipped
foreign event names, lag occurrences, and the matched test-owned event marker
without logging payload PII; ensure both sites cover the requested state
transitions and error paths.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: fd22d1dc-43bb-4672-9b6c-64b107b81474
📒 Files selected for processing (4)
.github/workflows/ci-lite.ymlCargo.tomlsrc/openhuman/learning/startup.rssrc/openhuman/web_chat/event_bus.rs
🚧 Files skipped from review as they are similar to previous changes (2)
- Cargo.toml
- .github/workflows/ci-lite.yml
…t-benchmark-5046 # Conflicts: # src/openhuman/learning/startup.rs # src/openhuman/web_chat/event_bus.rs
Brings tinyhumansai#5082 (learning subscriber test isolation — fixes the flaky coverage failure) and resolves the prettier format:check failure via main's formatted files. Cargo.lock resynced (--locked passes root + app/src-tauri); cargo fmt, prettier, and clippy -D warnings all clean locally. Adopts main's tauri-cef pin bump.
| .context("--repeat value")?; | ||
| } | ||
| "--out" => out = Some(PathBuf::from(it.next().context("--out needs a path")?)), | ||
| other => anyhow::bail!("unknown argument: {other}"), |
There was a problem hiding this comment.
--roster / --repeat are parsed here, but run_parent (called from main below) always uses the hardcoded DEFAULT_ROSTER_SIZES — only --repeat reaches it. A --roster 4 passed without --child is silently dropped. Since this is a benchmark people poke at by hand, consider either honouring --roster in parent mode or rejecting it there so it fails loudly instead of no-op'ing.
| - name: Measure embedded RSS (5 fresh procs x {1,8} agents) | ||
| run: ./target/release/rss-bench --out bench-rss.json | tee -a "$GITHUB_STEP_SUMMARY" | ||
|
|
||
| - name: Upload raw RSS samples |
There was a problem hiding this comment.
Nit: the cargo test / cargo build steps above route through scripts/ci-cancel-aware.sh, but this measure step does not. It's short and report-only so the blast radius is tiny, but a cancelled run won't have its watchdog stop the in-flight measurement. Minor consistency nit.
CodeGhost21
left a comment
There was a problem hiding this comment.
PR #5060 — feat(bench): embedded-RSS benchmark harness + report-only CI (#5046)
Walkthrough
Adds a reproducible steady-state RSS benchmark for an embedded openhuman_core agent roster: a new ungated proc_metrics lib module (Linux /proc/self/{status,smaps_rollup} sampling + aggregation + serde report + human summary), a default-OFF rss-bench binary that builds 1- and 8-agent rosters over a mock provider / no-op memory and measures 5 fresh child processes per roster, and a report-only rust-rss-bench CI job deliberately kept out of pr-ci-gate.needs. The shipped build is byte-identical (feature default-OFF, no new deps). The load-bearing logic lives in the ungated module so the standard coverage lane enforces it — a clean placement decision that keeps benchmark code out of the shipped binary.
Overall this is well-scoped, well-documented, and well-tested (11 proc_metrics unit tests + 2 gated fixture tests). The Rust RSS Benchmark (report-only) job passes, which exercises cargo test --features rss-bench --bin rss-bench and the stripped-release build — so the Agent::builder / Provider / Memory API usage in the new bin is verified to compile and run. No blocking code issues found.
Changes
| File | Summary |
|---|---|
src/openhuman/proc_metrics/mod.rs |
New ungated module: /proc parsers (&str-in, unit-tested), sample_self() (Linux-only, structured error off-Linux), roster aggregation, BenchReport serde + human_summary. |
src/bin/rss_bench.rs |
New default-OFF binary: mock provider / echo tool / NoopMemory fixture, roster builder, warm-up + settle, parent/child re-exec driver with per-child timeout + reap. |
Cargo.toml |
Registers the rss-bench bin (required-features = ["rss-bench"]) and the default-OFF rss-bench feature. |
src/openhuman/mod.rs |
pub mod proc_metrics;. |
.github/workflows/ci-lite.yml |
Adds the report-only rust-rss-bench job (not in the gate). |
Actionable comments (2)
💡 Refactor / suggestion
1. src/bin/rss_bench.rs:360-402 — --roster / --repeat are silently ignored in parent mode
parse_args() parses --roster and --repeat, but run_parent always uses the hardcoded DEFAULT_ROSTER_SIZES and only --repeat reaches it — a --roster 4 passed without --child is accepted and silently dropped. The module doc says the parent "re-execs itself --repeat times per roster size," so --repeat works, but --roster looks operative from the CLI and isn't. Since this is a benchmark people will poke at by hand, either honour --roster in parent mode (override DEFAULT_ROSTER_SIZES when present) or reject it there so it fails loudly instead of no-op'ing.
🧹 Nitpick
2. .github/workflows/ci-lite.yml (measure step) — inconsistent ci-cancel-aware.sh wrapping
The cargo test / cargo build steps route through scripts/ci-cancel-aware.sh, but the ./target/release/rss-bench --out bench-rss.json | tee ... measure step does not. It's short and report-only so the blast radius is tiny, but a cancelled run won't have its watchdog stop the in-flight measurement. Minor consistency nit.
Nitpicks
src/bin/rss_bench.rs:228—settle()is documented as a "No-op off Linux," but it still performs one 200 mssleepbefore breaking (firstsample_self()errors →last = 0, next readunwrap_or(last) = 0,abs_diff < 256→ break). Harmless; the doc slightly overstates.
Verified / looks good
proc_metricsparsers correctly avoid false matches (Pss:vsPss_Anon:/Pss_Dirty:via the trailing colon) and default missing keys to zero — both covered by tests.median_u64even-count path is overflow-safe (a + (b - a)/2) andper_agent_increment_kibguards the single-roster / zero-span cases withchecked_sub+saturating_sub. Tested.NoopMemoryfixture is the right call — the review history showscreate_memory({backend:"none"})still builds aUnifiedMemory+ cloud embedder, which would have inflated the baseline; injecting a zero-alloc no-op both fixes that and mirrors the host-supplied-memory embedding contract.- Child driver uses
kill_on_drop(true)+timeout(120s)aroundwait_with_output(which drains both pipes, no buffer deadlock) for a clean kill-and-reap on a stalled child. Sound. - Feature is genuinely default-OFF: the
Feature Forwarding Gatecheck passes, and the shipped build carries no new deps.
Notes for the author / CI status
Three checks are red, and after tracing each I believe none are caused by this PR's code — but they do block the merge, so please confirm/re-run:
- Scripts Self-Tests — fails on
core default gate not forwarded to the shell: tui. That's thetuifeature (from #5084, already onmain), notrss-bench. Pre-existing, unrelated. - Rust Core Coverage — fails inside
agent_harness_e2e(approval_gate_deny/approve/timeout,subagent_clarification_flow) — timing-sensitive e2e tests unrelated toproc_metrics. Note these ran at all because editingCargo.tomltrips the config-level full-suite fallback (per the two-lane CI model), so this PR is exposed to those flakes even though it doesn't touch them. - PR CI Gate — aggregate; red only because of the two above.
Because the coverage lane aborted on the unrelated flakes, diff-cover never got to evaluate the ≥80% diff-coverage gate on the actual changed lines. A re-run (or landing the flake fixes) is needed before the gate can go green. Leaving this as a comment rather than an approval only because of that red CI — the code itself LGTM.
…ell self-test The 'real shell manifest forwards every real core default' self-test asserted every default-ON gate is forwarded to the desktop shell, ignoring the INTENTIONALLY_NOT_FORWARDED allowlist that the CI checker respects. Once the tui gate (tinyhumansai#5084) became default-ON — a terminal subcommand deliberately not shipped in the Tauri shell — the two disagreed and Scripts Self-Tests failed with 'core default gate not forwarded to the shell: tui'. Move the allowlist into scripts/lib/feature-forwarding.mjs (one source of truth) and have the self-test skip allowlisted gates, matching the checker. The lane is path-gated so this was dormant on main; it surfaced here.
Reconcile dep-gating with main (now incl. tinyhumansai#5052/tinyhumansai#5060/tinyhumansai#5061/tinyhumansai#5062): - Cargo.toml: union gating (optional axum/sentry/ppt-rs + http-server/ inference/documents/crash-reporting features) with main's tower + tui deps and desktop-automation/tui features; default = union of both feature sets. - app/src-tauri: shell forwards crash-reporting/http-server/desktop-automation (feature-forwarding gate passes; tui allowlisted). - all_tests.rs: union of http-server + desktop-automation gate tests. - types.rs: keep main's redirect_links security denylist entry (tinyhumansai#5052). Cargo.lock + app/src-tauri/Cargo.lock resynced (--locked passes); cargo fmt, feature-forwarding gate, and clippy -D warnings all clean locally.
Summary
openhuman::proc_metricslib module: Linux/proc/self/{status,smaps_rollup}sampling + roster aggregation + serde report + human summary.rss-benchbinary (gated behind a default-OFFrss-benchfeature) that mirrors the OpenCompany embedding contract: a bareAgentbuilt viaAgent::builder()— noCoreBuilder, RPC, or background services.rust-rss-benchCI job uploads raw samples + a summary; it is deliberately not inpr-ci-gate.needs, so it can never fail a PR.Problem
To hold embedded
openhuman_corewithin the ≤20 MiB target / ≤30 MiB hard steady-state RSS cap for OpenCompany tenants, the budget must first be measurable and reproducible. There is no existing smaps/RSS harness in the repo, and the sibling gate/shed PRs (#5048/#5049/#5051) need a common baseline to measure their savings against. Per the issue, the measurement must land report-only first to establish runner variance before the gate becomes blocking.Solution
proc_metrics: OS-agnosticparse_status/parse_smaps_rollup(&strin — unit-tested without a live/proc);sample_self()is Linux-only and returns a structured error off-Linux rather than fabricating a reading. Reports RSS/PSS/private-clean+dirty/VmHWM/threads/binary-size, aggregated per roster (median RSS, min/max/mean, peak VmHWM).rss-bench: builds 1-agent and 8-agent rosters with an injected mock provider (no network),"none"memory backend, and a per-agent temp workspace; runs one deterministic warm-up turn per agent, settles, then measures 5 fresh child processes per roster (parent re-execs itself). Emits a raw JSON artifact + a Markdown summary.pr-ci-gate.needs+ a threshold step) are documented in the workflow comment; left report-only in this PR per the issue.Submission Checklist
proc_metricshas 9 unit tests (parsers, aggregation, serde round-trip, non-Linux error path); the gated bin has 2 fixture tests (roster construction + a warm-up turn that completes with no network).proc_metricsmodule, covered by its unit tests via the standard--libcoverage lane; the thin gated bin driver runs its fixture tests in therust-rss-benchjob.N/A: internal benchmark/CI infrastructure, no user-facing feature row.N/A: no matrix feature IDs.N/A: does not touch a release-cut surface (default-OFF feature).Closes #NNN—N/A: foundation slice (harness + report-only CI). The budget itself is met as Add the missing inference gate so whisper-rs and cpal stop linking into every build #5048/Gate the desktop-automation cluster, then extract it out of the core #5049/Remove zero-reference dependencies and unwired domains #5051 land and the gate flips blocking; see## Related.Impact
rss-benchbinary and a report-only Linux CI job. No runtime impact on the shipped desktop/library build (default-OFF, byte-identical, no new deps).Related
rust-rss-benchjob to blocking at 30 MiB once the shed/gate PRs land (seams documented inci-lite.yml).Changes since initial submission (review-driven)
bdf02f7b7) —BenchReport::per_agent_increment_kib()derives the marginal cost of agents 2–8 from the roster spread; addresses the "bounded per-agent growth" criterion.1ab0459cd, CodeRabbit) — each child spawns withkill_on_dropand a 120stimeoutaroundwait_with_output, so a stalled child is killed/reaped instead of blocking the run; stderr-only diagnostics for spawn/timeout/exit + child build/warm-up/settle/sample.Memoryfixture (56d30941f, Codex — real bug) —create_memory(MemoryConfig{ backend:"none" })does not select a no-op backend (create_memory_fullalways builds aUnifiedMemory+ cloud embedder), which was inflating the RSS baseline. Replaced with a zero-allocationNoopMemory, which also better mirrors the OpenCompany host-supplied-memory contract.AI Authored PR Metadata (required for Codex/Linear PRs)
Linear Issue
Commit & Branch
Summary by CodeRabbit
New Features
rss-benchcommand-line benchmark to measure steady-state RSS/PSS and related metrics across varying agent roster sizes.Chores
bench-rss.json, and uploads it as an artifact without impacting pull-request gating.