Skip to content

fix(core): restore the core build after the live-voice and tinymemory merges - #7056

Closed
oxoxDev wants to merge 14 commits into
tinyhumansai:mainfrom
oxoxDev:fix/main-compile-after-live-voice
Closed

oxoxDev wants to merge 14 commits into
tinyhumansai:mainfrom
oxoxDev:fix/main-compile-after-live-voice

Conversation

@oxoxDev

@oxoxDev oxoxDev commented Oct 7, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • main at 8c9c480a4e does not compile openhuman-core: six errors, all semantic collisions between changes that merged on 2026-10-07. Each was correct on its own branch. This PR fixes all six without changing behaviour.

  • It also fixes one test that came in with those merges but never ran, because the crate did not build.

  • It applies rustfmt to the two files cargo fmt --check rejects on main.

  • Merged with main after chore(vendor): pin tinymcp v0.4.0 and tinyskills v0.2.8 #7054. That PR already brought Cargo.lock to tinymemory 1.23.4, so this PR's lockfile commit is now a no-op and the lockfile matches main.

  • Restores the CI fix that merge 89e341c dropped (from 2680111):

    • rust-core-coverage installs node deps on a core-only change again. Without them scripts/mock-api-server.mjs cannot import ws, and every memory_v2_e2e test times out waiting for the mock backend.
    • rust-coverage.sh puts the selected toolchain first on PATH again, for the sandboxed acting-tool tests, and stops with an error when rustup cannot resolve it.
    • A new lanes test pins the node-deps install.
  • Refreshes crates/openhuman-app/Cargo.lock for tinymcp 0.4.0, tinyskills 0.2.8 and tinymemory 1.23.4. The desktop --locked dependency check failed because only the root lockfile had moved.

Problem

  • CI Fast fails on every PR opened against main (for example chore(vendor): pin tinymcp v0.4.0 and tinyskills v0.2.8 #7054), because the core build stops at:
    • core/runtime/context.rs:231: init_master_key().map_err(..)?, but init_master_key returns () on main.
    • memory/brain.rs:251: tinymemory 1.23.4 made Ingested::job an Option<BackgroundJob>.
    • memory/engine.rs:254: tinymemory 1.23.4 added EngineSettings::consolidation.
    • voice/live/session.rs:105 and voice/live/persist_tests.rs:30: the pinned tinyagents added CreateConversationThread::working_dir.
    • config/schema/types/config_clone.rs:14: the hand-written Config clone has no voice_live field.
  • The CI Gate status on main stayed green, so the break was not visible from main itself.

Solution

  • context.rs: call init_master_key() the way the keyring defines it. The Result-returning keyring hardening from 092787c is in main's history, but its content is not in the current tree (encrypted_file_backend.rs, lib.rs and openhuman-tui all use the () form). Restoring it is out of scope for a compile fix.
  • brain.rs: enqueue the belief build only when the engine returns one. None means the engine consolidates on its own.
  • engine.rs: consolidation: None, which keeps the engine's default (tinyhumans is always scheduled).
  • Live voice threads: working_dir: None, since a voice conversation has no working directory.
  • config_clone.rs: clone voice_live.
  • voice/live/ws_tests.rs: drop a futures import that the parent glob already brings in. It produced an unused-import warning.
  • config/schema/load_migration_tests.rs: load_or_init_disables_and_persists_legacy_memory_backend asserted the saved file contains no backend substring anywhere. Other sections legitimately write backend keys (observability backend = "otel", computer backend = "tinycomputer", voice mode = "backend"), and so does the migration's own legacy_backend_unsupported marker. It now parses the TOML and checks the [memory] table: engine = "" and no backend key.

Submission Checklist

If a section does not apply to this change, mark the item as N/A with a one-line reason. Do not delete items.

  • Tests added or updated (happy path + at least one failure / edge case): the legacy-memory migration test now asserts on the [memory] table it is about.
  • N/A: one-line call-site and initializer fixes; diff coverage is measured once the crate compiles in CI (diff coverage ≥ 80%)
  • N/A: no feature rows added, removed or renamed (coverage matrix)
  • N/A: no matrix rows change (feature IDs)
  • No new external network dependencies introduced
  • N/A: no release-cut surface changes (manual smoke checklist)
  • N/A: no tracking issue; this unblocks CI on open PRs such as chore(vendor): pin tinymcp v0.4.0 and tinyskills v0.2.8 #7054 (linked issue)

Impact

  • Desktop, CLI and embed builds: compile again on main.
  • Runtime behaviour: unchanged.
    • Memory ingestion skips enqueueing only when the engine reports no job.
    • Engine consolidation keeps its default.
    • Live voice threads carry no working directory.
  • Security note for reviewers: the keyring hardening in 092787c is still absent from the tree. That change makes startup fail when a configured master-key source is set but unusable, and rejects invalid-Unicode key variables. Restoring it would be its own change.

Related


AI Authored PR Metadata (required for Codex/Linear PRs)

Linear Issue

  • Key: N/A
  • URL: N/A

Commit & Branch

  • Branch: fix/main-compile-after-live-voice
  • Commit SHA: 16ab8fb

Validation Run

  • N/A: no frontend change (pnpm --filter openhuman-app format:check)
  • N/A: no frontend change (pnpm typecheck)
  • Focused tests:
    • cargo test -p openhuman --lib --features "$(bash scripts/ci/product-features.sh)" with RUST_MIN_STACK=67108864: 8304 passed.
    • Five failures, each macOS-only and in code this PR does not touch:
      • two attachment secure_open symlink tests: macOS tempdirs sit behind the /var → /private/var symlink;
      • sandbox::grants /proc: no /proc on macOS;
      • two shell sandbox / managed-python tests.
    • cargo test -p openhuman-embed: all pass.
    • cargo test -p openhuman-cli --features "<product>,bin-tools" --no-run: builds.
  • Rust fmt/check (if changed): cargo fmt --all -- --check clean; cargo clippy -p openhuman -p openhuman-cli -p openhuman-tinyhumans -- -D warnings with the product features is clean.
  • N/A: no Tauri change (Tauri fmt/check)

Validation Blocked

  • command: cargo test -p openhuman-cli --features "<product>,bin-tools" --test in_process_all -- domain_modules_e2e::legacy_memory_backend_is_off_and_persisted_through_json_rpc
  • error: memory_engine_get returns {"engine":"tinyhumans","status":"off","reason":"no TinyHumans backend is available"} after the test writes a legacy [memory] backend = "sqlite" config. The RPC is not reading the file the test wrote (it is the only config.toml under the harness $HOME). The unit-level migration test passes.
  • impact: this one in-process test, added with fix(memory): keep legacy local profiles off hosted memory #7041, still fails and is not addressed here. Which config the in-process RPC resolves after the session-store and context changes is for the owner to confirm.

Behavior Changes

  • Intended behavior change: none; compile and test fixes only.
  • User-visible effect: none.

Parity Contract

  • Legacy behavior preserved: yes.
  • Guard/fallback/dispatch parity checks: memory enqueue, engine consolidation default and config clone are field-for-field equivalent to each branch's intent.

Duplicate / Superseded PR Handling

  • Duplicate PR(s): none found
  • Canonical PR: this one
  • Resolution: N/A

Summary by CodeRabbit

  • Bug Fixes
    • Voice settings are now retained when configuration is copied.
    • Memory ingestion no longer schedules follow-up work when no job is available.
    • Voice conversations are created without an associated working directory.
    • Key initialization errors no longer stop startup at that step.
  • Tests
    • Updated checks verify saved memory settings and voice conversation persistence.
    • Added coverage for Rust core checks in self-hosted CI plans.

init_master_key returns () on main. The call site from the session-store ownership change used the Result-returning variant, so the core did not compile.
Ingested::job is now Option (None when the engine consolidates on its own), so only a present job is enqueued. EngineSettings gained consolidation; None keeps the engine default.
CreateConversationThread gained working_dir in the pinned tinyagents; live voice threads have none.
vendor/tinymemory is already at 1.23.4; the lockfile still named 1.23.1.
…he legacy backend key

Other sections legitimately write backend keys (observability, computer, voice) and the migration's own legacy_backend_unsupported marker, so a substring check over the whole file always failed.
@tinysweeper

tinysweeper Bot commented Oct 7, 2026 •

Copy link
Copy Markdown

Tiny Sweeper review

Tiny Sweeper completed its review; deterministic results follow.

State: Changes requested
Priority: high
Reviewed head: 16ab8fb2ea52
Updated: 1791362072 (Unix time)

Review snapshot

Change surface Files Review signal Count
Production 9 Active findings 6
Tests 4 Noted findings 0
Documentation 0 Resolved findings 7
Configuration 0 Pending checks/questions 4

Completeness: Complete
Test assessment: Test coverage is assessed from changed tests and lane evidence; execution is not claimed without trusted check data.

What changed

No supported behavioral explanation was produced.

Features

  • Modified — init_master_key call simplified in core runtime startup: `init_master_key` is now called without mapping a Result, adapting to its signature change (reported as confirmed by its definition). Master-key initialization continues to run before config/credential operations that need decryption. (crates/openhuman-core/src/core/runtime/context.rs#impl CoreContext {)
  • Internal refactor — EngineSettings construction updated with consolidation field: A `consolidation: None` field is set in the EngineSettings built for a dynamically-credentialed engine, adapting the memory engine to the new tinymemory settings shape. (crates/openhuman-core/src/memory/engine.rs#fn resolve_tinyhumans(config: &Config) -> Binding {)
  • Internal refactor — Config Clone now covers voice_live: The hand-written `Clone for Config` implementation now clones the `voice_live` field, keeping the clone complete after the live-voice merge added that field. (crates/openhuman-core/src/config/schema/types/config_clone.rs#impl Clone for Config {)
  • Modified — Strengthened config migration test assertions: The load-migration test now parses the saved config.toml into a `toml::Table` and asserts on the `memory` section (engine equals empty string, no backend key) structurally instead of via substring matching, making the migration assertions less brittle. (crates/openhuman-core/src/config/schema/load_migration_tests.rs#backend = "sqlite")
  • Internal refactor — Formatting and import cleanups: rustfmt reflows in `clear_if` (session store) and `previous_session_store` (embed runtime), and removal of an unused `futures` import from the websocket tests module; no behavioural impact. (crates/openhuman-core/src/agent/session_store/mod.rs#pub fn clear() -> bool {, crates/openhuman-embed/src/runtime/mod.rs#impl Runtime {, crates/openhuman-core/src/voice/live/ws_tests.rs#fn parses_only_a_start_frame_first() {)

Tests

  • strengthened existing test — The config-load migration test verifies that a saved config serializes `memory.engine` as an empty string and omits the `backend` key, by parsing the saved TOML into a table and inspecting the `memory` section structurally.: A genuine strengthening of an existing migration assertion; no evidence in the review indicates it was executed or passed. (crates/openhuman-core/src/config/schema/load_migration_tests.rs#backend = "sqlite")

Findings

  • high · critique · Resolve the toolchain without depending on RUSTUP_HOME — This still invokes `rustup which cargo` before adding the toolchain directory to `PATH`. When `RUSTUP_HOME` is omitted and the rustup metadata is installed outside `$HOME`, rustup (scripts/ci/rust\-coverage\.sh:17)
  • high · security · Resolve Cargo without depending on RUSTUP_HOME — `rustup which cargo` is itself a rustup operation and uses rustup's toolchain configuration, which is located through `RUSTUP_HOME` (or the default under `HOME`). In the sandbox de (scripts/ci/rust\-coverage\.sh:17)
  • medium · description · Add a test that ingest with no job does not enqueue — The `None` path — engine consolidates on its own, nothing enqueued — is still untested. The PR changes this exact branch and the migration test coverage work in the PR does not cov (\(pull request description\))
  • medium · e2e · Ingest with no job has no end-to-end test — The diff changes enqueue to run only when a job exists (`if let Some(job) = ingested.job { jobs::enqueue(...) }`), but no end-to-end test exercises this path. An end-to-end test wo (crates/openhuman\-core/src/memory/brain\.rs:251)

Previously reported and still active

  • Add an end-to-end test for ingest with no job
  • Ingest with no job still has no end-to-end test

Resolved this pass

  • Resolve the toolchain without depending on RUSTUP_HOME
  • Resolve the toolchain without depending on RUSTUP\_HOME
  • Resolve the toolchain without depending on RUSTUP_HOME
  • Resolve the toolchain without depending on RUSTUP_HOME
  • Resolve the toolchain without depending on RUSTUP_HOME
  • Resolve the toolchain without depending on RUSTUP\_HOME
  • Resolve the toolchain without depending on RUSTUP_HOME

Pending checks: Rust E2E (mock backend), Build Playwright E2E Artifact, E2E (Playwright / web lane), Desktop E2E (full suite, 3 OS)

Before merge

  • Address carried finding Add an end-to-end test for ingest with no job.
  • Address carried finding Ingest with no job still has no end-to-end test.
  • Address Resolve the toolchain without depending on RUSTUP_HOME (scripts/ci/rust\-coverage\.sh).
  • Address Resolve Cargo without depending on RUSTUP_HOME (scripts/ci/rust\-coverage\.sh).
  • Wait for Rust E2E (mock backend), Build Playwright E2E Artifact, E2E (Playwright / web lane), Desktop E2E (full suite, 3 OS).

How this fits together

flowchart LR
  n0["CoreContext<br/>changed"]:::changed
  n1["Runtime<br/>changed"]:::changed
  n2["DomainSet"]:::impacted
  n3["join"]:::impacted
  n4["new"]:::impacted
  n5["WorkspaceBinding"]:::impacted
  n6["init_with_config"]:::impacted
  n0 -->|uses| n2
  n0 -->|uses| n5
  n1 -->|uses| n2
  n4 -->|uses| n2
  n6 -->|uses| n0
  n6 -->|uses| n2
  n6 -->|calls| n3
  n6 -->|calls| n4
  n6 -->|uses| n5
  classDef changed fill:#0d4429,stroke:#238636,color:#e6edf3
  classDef impacted fill:#161b22,stroke:#6e7681,color:#c9d1d9
  classDef flagged fill:#5a1e02,stroke:#d93f0b,color:#ffffff
  classDef blocking fill:#67060c,stroke:#f85149,color:#ffffff
Loading
Agent review details

critique

  • Conclusion: Failure
  • Scope reviewed: all assigned evidence
  • Lane summary: The change attempts to bypass the rustup proxy by prepending the selected toolchain, but it still needs rustup metadata to discover that toolchain. It is not safe to merge for sandboxed runs where RUSTUP_HOME is unavailable. (12 earlier finding(s) still open) _Code retrieval was unavailable (model: ladder embeddings returned 400 Bad Request: {"error":{"message":"unknown ladder vectors; known ladders are flash (also chat-v1, flash-v1), instant (also no-think, instant-v1), reasoning (also deepseek), max-reasoning (also max-reasoning-v1), deepseek-flash (also reasoning-v1, agentic-v1), deep (also luna), scribe, uncensored, vectors-oai3 (also embeddings-oai3-v1), vision (also vision-v1, multimodal-v1), image (also images-v1, image-v1), vi), so this review saw the diff alone._ _Memory was unavailable (model: cortex: v1/recall: error sending request for url (http://cortexdb:3141/v1/recall\)\), so this review ran without it._
  • Evidence: scripts/ci/rust\-coverage\.sh — Resolve the toolchain without depending on RUSTUP_HOME

security

  • Conclusion: Failure
  • Scope reviewed: all assigned evidence
  • Lane summary: The change tries to bypass the rustup proxy by prepending the selected toolchain directory, but it still resolves that directory through rustup and therefore does not remove the sandbox's RUSTUP_HOME dependency. It is not safe to merge as written. (13 earlier finding(s) still open) (1 observation(s) grouped into shared inline comments) _Code retrieval was unavailable (model: ladder embeddings returned 400 Bad Request: {"error":{"message":"unknown ladder vectors; known ladders are flash (also chat-v1, flash-v1), instant (also no-think, instant-v1), reasoning (also deepseek), max-reasoning (also max-reasoning-v1), deepseek-flash (also reasoning-v1, agentic-v1), deep (also luna), scribe, uncensored, vectors-oai3 (also embeddings-oai3-v1), vision (also vision-v1, multimodal-v1), image (also images-v1, image-v1), vi), so this review saw the diff alone._ _Memory was unavailable (model: cortex: v1/recall: error sending request for url (http://cortexdb:3141/v1/recall\)\), so this review ran without it._
  • Evidence: scripts/ci/rust\-coverage\.sh — Resolve Cargo without depending on RUSTUP_HOME

tests

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Lane summary: The incremental change is a toolchain-resolution guard in rust-coverage.sh that errors out when rustup cannot resolve the selected cargo, which is the direct fix for the earlier "Resolve the toolchain without depending on RUSTUP_HOME" finding; the script change is CI-only shell with no testable behavior in this diff. The other prior findings (ingest enqueue behavior and its test coverage) are addressed by the separately-listed behavioral diff: brain.rs now guards `jobs::enqueue` behind `if let Some(job) = ingested.job`, so ingest with no job no longer enqueues, and the missing end-to-end coverage for that path remains the open medium finding. The lanes-plan change adds a pnpm-install for core-only coverage, with a matching new test asserting the dependency wiring. No new defects found in the incremental commit. (1 already reported on an earlier push) (3 earlier finding(s) still open) _Code retrieval was unavailable (model: ladder embeddings returned 400 Bad Request: {"error":{"message":"unknown ladder vectors; known ladders are flash (also chat-v1, flash-v1), instant (also no-think, instant-v1), reasoning (also deepseek), max-reasoning (also max-reasoning-v1), deepseek-flash (also reasoning-v1, agentic-v1), deep (also luna), scribe, uncensored, vectors-oai3 (also embeddings-oai3-v1), vision (also vision-v1, multimodal-v1), image (also images-v1, image-v1), vi), so this review saw the diff alone._ _Memory was unavailable (model: cortex: v1/recall: error sending request for url (http://cortexdb:3141/v1/recall\)\), so this review ran without it._

commits

  • Conclusion: Neutral
  • Scope reviewed: all assigned evidence
  • Positive: The commits lane found nothing sensitive in what the pull request commits.
  • Lane summary: Nothing sensitive found in what this pull request commits.

description

  • Conclusion: Success
  • Scope reviewed: all assigned evidence
  • Positive: The description lane reports the toolchain block resolves the high-severity RUSTUP_HOME finding.
  • Lane summary: The PR does what its description says: six compile fixes, rustfmt, and restores the CI node-deps and toolchain setup, all of which match the diff. The toolchain block added since the last review resolves the high-severity RUSTUP_HOME finding and now stops with an error instead of silently running through the rustup proxy. Two prior findings about a missing no-job ingest test remain unfixed and unaddressed in this revision, and are kept. (5 earlier finding(s) still open) _Code retrieval was unavailable (model: ladder embeddings returned 400 Bad Request: {"error":{"message":"unknown ladder vectors; known ladders are flash (also chat-v1, flash-v1), instant (also no-think, instant-v1), reasoning (also deepseek), max-reasoning (also max-reasoning-v1), deepseek-flash (also reasoning-v1, agentic-v1), deep (also luna), scribe, uncensored, vectors-oai3 (also embeddings-oai3-v1), vision (also vision-v1, multimodal-v1), image (also images-v1, image-v1), vi), so this review saw the diff alone._ _Memory was unavailable (model: cortex: v1/recall: error sending request for url (http://cortexdb:3141/v1/recall\)\), so this review ran without it._
  • Evidence: \(pull request description\) — Add a test that ingest with no job does not enqueue

e2e

  • Conclusion: Neutral
  • Scope reviewed: all assigned evidence
  • Lane summary: The core-only pnpm-install wiring in lanes-plan.mjs is now guarded by a test asserting both ex63 and hosted profiles run the install before rust-core-coverage, so the mock backend can start; the migration assertion rewrite and the rust-coverage RUSTUP_HOME workaround have no external surface needing an e2e harness. The 'ingest with no job' end-to-end test remains missing from this revision — the only change touching that path still calls jobs::enqueue unconditionally. Waiting on end-to-end jobs: `Rust E2E (mock backend)`, `Build Playwright E2E Artifact`, `E2E (Playwright / web lane)`, `Desktop E2E (full suite, 3 OS)`. (9 earlier finding(s) still open)
  • Unresolved questions/checks: Rust E2E (mock backend), Build Playwright E2E Artifact, E2E (Playwright / web lane), Desktop E2E (full suite, 3 OS)
  • Evidence: crates/openhuman\-core/src/memory/brain\.rs — Ingest with no job has no end-to-end test
Evidence and run details
  • Models: gpt-5.6-luna, glm-5.3-flash
  • Spend: $0.002588
  • Tokens: 84399 input · 7514 output · 9428 cached · 0 embedding
Head State Pass summary
cb6ed6dd1a8f pending 1 active finding(s), 0 resolved finding(s) (at 1791359030)
990f27c4d903 pending 9 active finding(s), 4 resolved finding(s) (at 1791359326)
84027bef3a50 changes requested 3 active finding(s), 3 resolved finding(s) (at 1791360821)
542d2185edb0 changes requested 4 active finding(s), 4 resolved finding(s) (at 1791361734)
16ab8fb2ea52 changes requested 4 active finding(s), 7 resolved finding(s) (at 1791362072)

tinysweeper 0.1.0

@coderabbitai

coderabbitai Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: b33a8e68-7add-4894-99de-03fad13c80ff
📥 Commits

Reviewing files that changed from the base of the PR and between 990f27c and 16ab8fb.

⛔ Files ignored due to path filters (1)
  • crates/openhuman-app/Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • scripts/__tests__/self-hosted-lanes.test.mjs
  • scripts/ci/rust-coverage.sh
  • scripts/ci/self-hosted/lanes-plan.mjs

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 8 remain after this review.


📝 Walkthrough

Walkthrough

The changes update core configuration, runtime, memory, and live voice code. They also adjust Rust coverage lane dependencies and the coverage script’s Cargo toolchain selection.

Changes

Core updates

Layer / File(s) Summary
Configuration cloning and migration assertions
crates/openhuman-core/src/config/schema/types/config_clone.rs, crates/openhuman-core/src/config/schema/load_migration_tests.rs
Config::clone now copies voice_live. The migration test parses TOML and checks that memory.engine is empty and memory.backend is absent.
Core runtime and memory updates
crates/openhuman-core/src/core/runtime/context.rs, crates/openhuman-core/src/memory/brain.rs, crates/openhuman-core/src/memory/engine.rs, crates/openhuman-core/src/agent/session_store/mod.rs, crates/openhuman-embed/src/runtime/mod.rs
init_with_config invokes init_master_key without handling its result. Memory ingestion enqueues only present jobs, and TinyHumans settings set consolidation to None. The session-store lock acquisition is condensed, and the embed runtime parameter is reformatted.
Live voice thread initialization and tests
crates/openhuman-core/src/voice/live/session.rs, crates/openhuman-core/src/voice/live/persist_tests.rs, crates/openhuman-core/src/voice/live/ws_tests.rs
New voice threads and a persistence-test fixture set working_dir to None. The WebSocket tests remove the SinkExt and StreamExt imports.

Rust coverage CI

Layer / File(s) Summary
Coverage lane dependencies
scripts/ci/self-hosted/lanes-plan.mjs, scripts/__tests__/self-hosted-lanes.test.mjs
The ex63 and hosted plans add pnpm-install checks for Rust-core changes. Rust-core coverage depends on the profile-appropriate install check and test-modules. Tests cover both profiles.
Coverage toolchain PATH setup
scripts/ci/rust-coverage.sh
When rustup is available, the script prepends the directory of the selected Cargo binary to PATH. If Cargo resolution fails, it reports RUSTUP_HOME and exits with status 1.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Suggested reviewers: senamakel

Merge Risk: ⚪ Minimal · up to 16ab8

This change restores the core build and adjusts the Rust coverage CI lane prerequisites. No concrete merge-blocking risk was identified in the supplied evidence.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 16ab8

The CI execution environment changes, but existing isolation controls remain in place. No introduced security issue was established. Runner permissions and background-job recovery behavior remain incompletely verified.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The demonstrated exposure is executable lookup in coverage and descendant test processes, including sandboxed shell children. An attacker-controlled rustup could influence the selected directory, but control of the inherited runner PATH already affects existing CI commands; no additional privilege gain was established. Actual runner permissions remain unverified.

Trust Boundaries and Controls

  • observed — The shell child clears its environment and reconstructs allowlisted variables, including PATH, while retaining existing sandbox checks. The coverage change selects direct toolchain binaries rather than forwarding RUSTUP_HOME into the write-confined jail. Repository and container configuration pin the toolchain and configure its installation location.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 81.82% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 11 functions across 12 files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the main change: restoring the core build after merge-related compile errors.
✨ Finishing Touches 💡 2
⚔️ Resolve merge conflicts 💡
  • Resolve merge conflict in branch fix/main-compile-after-live-voice
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

A rabbit checks the lanes at dawn
Then watches Cargo hop along
The config keeps its voice in tune
New threads leave working dirs at noon
Small jobs wait until they’re there
And carrots roll through code with care

Comment @coderabbitai help to get the list of available commands.

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tinysweeper found nothing blocking. Approving.

             $0.0035 · 205,354 in / 10,174 out · 25,600 cached (12%) · gpt-5.6-luna, glm-5.3-flash
critique:    $0.0015 · 95,581 in  / 4,005 out  · 12,184 cached (13%) · gpt-5.6-luna
security:    $0.0012 · 80,842 in  / 2,844 out  · 8,936 cached (11%)  · gpt-5.6-luna
tests:       $0.0001 · 11,400 in  / 937 out    · 3,072 cached (27%)  · glm-5.3-flash
description: $0.0000 · 6,207 in   / 97 out     · 1,408 cached (23%)  · glm-5.3-flash
e2e:         $0.0001 · 7,526 in   / 681 out    · 0 cached (0%)       · glm-5.3-flash

.ingest_with(document, WriteOptions::accepted())
.await?;
jobs::enqueue(config, layout(config).root(), vec![ingested.job]).await;
if let Some(job) = ingested.job {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority medium tests likely

Add a test that ingest with no job does not enqueue

The guard changes behaviour: when ingest_with yields no job, jobs::enqueue is no longer called. Previously the job list was enqueued unconditionally, so a regression back to the old call (or a future refactor of ingested.job into an Option-unwrapping helper) would silently enqueue nothing or panic. Nothing in the diff or existing tests pins either half of the contract: that a job-producing ingest still enqueues, and that a job-less ingest does not. A sibling test asserting the jobs queue is empty (and, for the job-producing path, contains the ingested job) would fail if this regresses.

[RULE] missing-test-coverage ·

@tinysweeper tinysweeper Bot added the priority: p2 Soon. Real but survivable — a rough edge, a gap, a thing that will bite later. label Oct 7, 2026

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

tinysweeper found nothing blocking. Approving.

             $0.0163 · 321,480 in / 22,266 out · 29,410 cached (9%)  · gpt-5.6-luna, glm-5.3-flash
critique:    $0.0083 · 154,150 in / 10,531 out · 16,775 cached (11%) · gpt-5.6-luna, glm-5.3-flash
security:    $0.0076 · 129,294 in / 7,963 out  · 12,507 cached (10%) · gpt-5.6-luna
tests:       $0.0001 · 5,671 in   / 411 out    · 0 cached (0%)       · glm-5.3-flash
description: $0.0001 · 6,519 in   / 378 out    · 0 cached (0%)       · glm-5.3-flash
e2e:         $0.0002 · 16,249 in  / 1,255 out  · 64 cached (0%)      · glm-5.3-flash

.unwrap();
assert!(saved.contains("engine = \"\""));
assert!(!saved.contains("backend"));
let saved: toml::Table = toml::from_str(&saved).unwrap();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority medium critique confident

Add a test that ingest with no job does not enqueue

This revision only parses the saved TOML and checks the migrated memory fields; it still does not exercise the ingest path with no job or assert that no enqueue occurs. The previously reported behavior remains unprotected by a regression test, so add a focused test covering that case.

[RULE] missing-test ·

// needs to decrypt secrets. No-op if already called (e.g. from
// run_core_from_args for the CLI).
crate::security::keyring::init_master_key().map_err(anyhow::Error::msg)?;
crate::security::keyring::init_master_key();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority medium critique confident

Add a test that ingest with no job does not enqueue

This revision only changes keyring initialization and does not address the previously identified case where ingest without a job must not enqueue work. Keep the existing concern and add a regression test covering that behavior.

[RULE] missing-test ·

parent_thread_id: None,
labels: None,
personality_id: None,
working_dir: None,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority medium critique confident

Add a test that ingest with no job does not enqueue

This revision does not add the previously requested regression test proving that ingest without a job does not enqueue work. The behavior remains unprotected against regressions; add the test in the appropriate sibling test module. This is reported as a late finding because the pull request changed only the thread initializer, not the ingest path, and the gap is unchanged from the earlier review.

[RULE] missing-regression-test ·

let mut installed = PROVIDER
.write()
.unwrap_or_else(PoisonError::into_inner);
let mut installed = PROVIDER.write().unwrap_or_else(PoisonError::into_inner);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority medium critique confident

Add a test that ingest with no job does not enqueue

The earlier regression-test concern is still unresolved: this revision adds no test covering the no-job ingest path and its enqueue behavior. Keep the test in the sibling test module so a future change cannot regress this contract unnoticed.

[RULE] missing-regression-test ·

.ingest_with(document, WriteOptions::accepted())
.await?;
jobs::enqueue(config, layout(config).root(), vec![ingested.job]).await;
if let Some(job) = ingested.job {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority medium security confident

Add a test that ingest with no job does not enqueue

This new branch changes queueing behavior when ingestion returns no background job, but the diff adds no regression test covering that case. Add a test that verifies ingestion with no job leaves the queue unchanged, so a future refactor cannot reintroduce an empty or invalid enqueue.


Additional e2e observation

priority medium uncertain

Ingest with no job has no end-to-end test

[RULE] e2e-uncovered

This change alters observable behaviour: when ingested.job is None, nothing is enqueued, where previously an enqueue always happened. No end-to-end test drives this. A test would have to run the system end to end, ingest a document that produces no job (the replayed/duplicate path), and assert that no job was added to the jobs queue — for example via an RPC surface that lists pending jobs, or by checking the jobs store after an ingest through the running core. The lexical candidates mentioning 'memory' (chat-agent-plan.spec.ts) do not touch ingest or the jobs queue.

[RULE] missing-test ·

parent_thread_id: None,
labels: None,
personality_id: None,
working_dir: None,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority medium security confident

Add a test that ingest with no job does not enqueue

The missing-thread test still only awaits save and does not inspect the resulting job queue. A regression that enqueues work despite having no thread would pass unnoticed; assert that the queue remains empty after this save.


Additional critique observation

priority medium confident

Add a test that ingest with no job does not enqueue

[RULE] missing-behavior-test

This fixture update keeps the missing-thread test limited to verifying that save returns without failing. It still does not assert that ingesting a transcript without an associated job leaves the job queue unchanged, so a regression that enqueues work in this path would pass the suite. Add an assertion against the relevant queue/store after the no-thread save.

[RULE] missing-regression-test ·

coderabbitai[bot]
coderabbitai Bot previously approved these changes Oct 7, 2026
…skills 0.2.8 and tinymemory 1.23.4

The root lockfile moved with those pins; crates/openhuman-app/Cargo.lock did not, so the desktop --locked dependency check failed.
… PATH for sandboxed tests

Merge 89e341c dropped both from 2680111: rust-core-coverage again needs pnpm-install (the mock backend imports ws) on a core-only change, and rust-coverage.sh again puts the selected toolchain first on PATH so sandboxed acting-tool tests need no rustup home writes.

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes: 1 lane(s) blocking, worst finding is high.

Fix or reply to the findings below and push. The next review clears this automatically once they are gone — you should not need to dismiss anything by hand.

             $0.0087 · 182,836 in / 12,755 out · 15,289 cached (8%) · gpt-5.6-luna, glm-5.3-flash
critique:    $0.0042 · 71,992 in  / 5,459 out  · 9,932 cached (14%) · gpt-5.6-luna, glm-5.3-flash
security:    $0.0040 · 69,648 in  / 3,545 out  · 5,357 cached (8%)  · gpt-5.6-luna
tests:       $0.0001 · 6,936 in   / 145 out    · 0 cached (0%)      · glm-5.3-flash
description: $0.0001 · 8,211 in   / 376 out    · 0 cached (0%)      · glm-5.3-flash
e2e:         $0.0001 · 9,307 in   / 826 out    · 0 cached (0%)      · glm-5.3-flash

# to create ~/.rustup inside the write-confined jail. Put the already selected
# toolchain binaries first; cargo --version then needs no rustup home writes.
# Keep cargo-installed subcommands on PATH after the toolchain directory.
if command -v rustup >/dev/null 2>&1; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority high critique confident

Resolve the toolchain without depending on RUSTUP_HOME

When RUSTUP_HOME is absent, rustup which cargo normally looks for its metadata under the default $HOME/.rustup. In the sandbox described by this script, that directory is not where the container's toolchain is installed, so the command fails, the conditional silently skips the PATH update, and subsequent cargo invocations still resolve to the rustup proxy and may try to write the confined home directory. Use the container's configured toolchain path directly or explicitly provide the correct rustup home before resolving cargo; do not silently continue when resolution fails.

[RULE] rustup-home-resolution ·

}
});

test("a core-only change installs the node deps the mock backend needs before rust coverage", () => {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority medium security confident

Add an end-to-end test for ingest with no job

This new test only verifies CI dependency ordering; it does not exercise the previously identified ingest-without-a-job path. That path still lacks an end-to-end regression test, so the behavior can regress without detection. Add coverage that invokes ingest with no job and asserts that no enqueue occurs.

[RULE] missing-end-to-end-test ·

@tinysweeper tinysweeper Bot added priority: p1 Next. Wrong behaviour a user will hit, or a security weakness behind a condition. and removed priority: p2 Soon. Real but survivable — a rough edge, a gap, a thing that will bite later. labels Oct 7, 2026

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes: 2 lane(s) blocking, worst finding is high.

Fix or reply to the findings below and push. The next review clears this automatically once they are gone — you should not need to dismiss anything by hand.

             $0.0030 · 87,531 in / 8,881 out · 11,184 cached (13%) · gpt-5.6-luna, glm-5.3-flash
critique:    $0.0014 · 29,061 in / 2,428 out · 5,862 cached (20%)  · gpt-5.6-luna, glm-5.3-flash
security:    $0.0012 · 16,541 in / 2,607 out · 5,130 cached (31%)  · gpt-5.6-luna
tests:       $0.0001 · 7,095 in  / 502 out   · 0 cached (0%)       · glm-5.3-flash
description: $0.0001 · 8,385 in  / 471 out   · 64 cached (1%)      · glm-5.3-flash
e2e:         $0.0001 · 9,466 in  / 552 out   · 64 cached (1%)      · glm-5.3-flash

# toolchain binaries first; cargo --version then needs no rustup home writes.
# Keep cargo-installed subcommands on PATH after the toolchain directory.
if command -v rustup >/dev/null 2>&1; then
if COV_TOOLCHAIN_CARGO="$(rustup which cargo 2>/dev/null)"; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority high critique confident

Resolve the toolchain without depending on RUSTUP_HOME

When RUSTUP_HOME is unset and the rustup proxy is on PATH, rustup which cargo can itself fail because it cannot locate the rustup toolchain state. This takes the else branch, which explicitly retains the proxy on PATH; the subsequent cargo invocations then retry the same proxy and may attempt to create ~/.rustup inside the write-confined sandbox. The fallback needs to locate or receive the already-installed toolchain binaries without invoking rustup state, rather than continuing with the proxy.


Additional security observation

priority high confident

Resolve the toolchain without depending on RUSTUP_HOME

[RULE] toolchain-resolution

When RUSTUP_HOME is unset, rustup which cargo normally cannot locate the installation outside HOME. The fallback explicitly leaves the rustup proxy on PATH, which then tries to access or create ~/.rustup inside the write-confined sandbox and can make the coverage job fail. Resolve the installed toolchain through the container's known toolchain location, or fail before invoking the proxy rather than treating this fallback as successful.

[RULE] toolchain-resolution ·

.ingest_with(document, WriteOptions::accepted())
.await?;
jobs::enqueue(config, layout(config).root(), vec![ingested.job]).await;
if let Some(job) = ingested.job {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority medium e2e likely

Ingest with no job still has no end-to-end test

The behavioural change — ingest now skips jobs::enqueue when ingested.job is None — has an external surface: an ingested document whose job is not queued must not produce background processing, while the receipt/view is still returned. No end-to-end harness in this diff or the candidate list drives ingest through the running core and asserts that no job appears (or that enqueue is skipped) for the no-job case; the candidates only mention memory in routing strings. A test would have to ingest a document through the running system and observe the job queue. Carried over from the earlier review at the same severity; the code is unchanged since.

[RULE] e2e-uncovered ·

@tinysweeper tinysweeper Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Requesting changes: 2 lane(s) blocking, worst finding is high.

Fix or reply to the findings below and push. The next review clears this automatically once they are gone — you should not need to dismiss anything by hand.

             $0.0026 · 84,399 in / 7,514 out · 9,428 cached (11%) · gpt-5.6-luna, glm-5.3-flash
critique:    $0.0011 · 22,191 in / 1,848 out · 5,862 cached (26%) · gpt-5.6-luna, glm-5.3-flash
security:    $0.0009 · 12,241 in / 2,154 out · 3,566 cached (29%) · gpt-5.6-luna
tests:       $0.0001 · 7,075 in  / 397 out   · 0 cached (0%)      · glm-5.3-flash
description: $0.0001 · 8,362 in  / 450 out   · 0 cached (0%)      · glm-5.3-flash
e2e:         $0.0001 · 9,446 in  / 326 out   · 0 cached (0%)      · glm-5.3-flash

# to create ~/.rustup inside the write-confined jail. Put the already selected
# toolchain binaries first; cargo --version then needs no rustup home writes.
# Keep cargo-installed subcommands on PATH after the toolchain directory.
if command -v rustup >/dev/null 2>&1; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority high critique confident

Resolve the toolchain without depending on RUSTUP_HOME

This still invokes rustup which cargo before adding the toolchain directory to PATH. When RUSTUP_HOME is omitted and the rustup metadata is installed outside $HOME, rustup cannot find the selected toolchain and this branch exits with an error; the script therefore still cannot run in the sandbox described by the comment. Resolve the toolchain from the container's known installation or otherwise arrange a toolchain path without requiring rustup metadata in the confined home.


Additional security observation

priority high confident

Resolve Cargo without depending on RUSTUP_HOME

[RULE] rustup-home-dependency

rustup which cargo is itself a rustup operation and uses rustup's toolchain configuration, which is located through RUSTUP_HOME (or the default under HOME). In the sandbox described by this script, that configuration is not forwarded and may not exist at the default location, so this branch exits before coverage can run—the same toolchain-resolution failure this change is intended to avoid. Locate the already-installed toolchain directly or otherwise provide a rustup-independent Cargo path instead of invoking rustup which.

[RULE] toolchain-resolution ·

.ingest_with(document, WriteOptions::accepted())
.await?;
jobs::enqueue(config, layout(config).root(), vec![ingested.job]).await;
if let Some(job) = ingested.job {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

priority medium e2e likely

Ingest with no job has no end-to-end test

The diff changes enqueue to run only when a job exists (if let Some(job) = ingested.job { jobs::enqueue(...) }), but no end-to-end test exercises this path. An end-to-end test would have to drive an ingest that produces no job and assert no file appears under layout(config).root() — the branch's behavioural surface (whether the jobs queue stays empty when the engine consolidation is disabled) is only reachable through the running system. Coverage so far is unit-level only.

[RULE] missing-e2e-coverage ·

@oxoxDev

oxoxDev commented Oct 7, 2026

Copy link
Copy Markdown
Collaborator Author

Superseded by #7058, which restored main (compile fixes, keyring hardening, CI node install and toolchain PATH, app lockfile). Closing.

@oxoxDev oxoxDev closed this Oct 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

priority: p1 Next. Wrong behaviour a user will hit, or a security weakness behind a condition.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant