Skip to content

feat(core): runtime DomainSet composition axis — group-tagged registry + ambient filter (#4796)#4808

Merged
senamakel merged 17 commits into
tinyhumansai:mainfrom
oxoxDev:feat/4796-domainset-filter-seam
Jul 13, 2026
Merged

feat(core): runtime DomainSet composition axis — group-tagged registry + ambient filter (#4796)#4808
senamakel merged 17 commits into
tinyhumansai:mainfrom
oxoxDev:feat/4796-domainset-filter-seam

Conversation

@oxoxDev

@oxoxDev oxoxDev commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Adds DomainSet — a second runtime-composition axis on CoreBuilder (sibling to the existing ServiceSet) that selects which domain families exist at runtime, one flag per DomainGroup.
  • Tags every controller with its DomainGroup at the single registration site in src/core/all.rs (via a GroupedController wrapper — the ~90 domain modules stay untouched), and filters the live surface by the ambient CoreContext::domains().
  • Five gated surfaces honor the selector: controllers (/schema + dispatch), agent tools, workspace stores, and event-bus subscribers. A gated domain becomes unknown-method, its tools absent, its stores/subscribers uninitialized.
  • Presets: full() (default — byte-identical to pre-PR registration; the desktop app is unchanged), harness() (agent + memory + threads + config + security), none().
  • Prereq seam for the epic (Feature gates for core subsystems — tracking (lightweight harness builds) #4795): the per-gate Cargo [features] children (feat(core): feature gate — flows/workflows #4797feat(core): feature gate — media generation #4804) compose with this runtime axis.

Problem

Slim / harness builds of the core need a way to boot with only a subset of domain families live, without forking the registration path or risking any change to the shipped desktop runtime. Before this PR the domain surface was all-or-nothing: every controller, agent tool, store, and subscriber registered unconditionally.

Solution

  • DomainSet (src/core/runtime/builder.rs): Copy struct, one bool per DomainGroup, allows() accessor, full()/harness()/none() presets, threaded through CoreBuilder::build()CoreContext.
  • DomainGroup + GroupedController (src/core/all.rs): registry entries carry their owning group; all_registered_controllers() / all_controller_schemas() / dispatch filter by the ambient CoreContext::domains(). With no ambient context (⇒ full, no filter) the public surface is byte-identical to the raw registry — proven by full_registration_is_byte_identical.
  • Store + subscriber gating extracted into pure decision values — StoreInitPlan (context.rs) and DomainSubscriberPlan (jsonrpc.rs) — that the registrars consume and tests assert directly. This gives the store→group and subscriber→group mappings a single source of truth and lets the gating be verified without touching process-global store/bus state.
  • Agent tool surface (tools/ops.rs) post-filtered by a tool_group() classifier against the ambient DomainSet.
  • examples/embed_headless.rs switched to DomainSet::harness().

Design note: tokenjuice is kept always-on / Platform (it is the token-compression content-router that runs on every agent tool output — agent-loop critical, not crypto), despite the epic listing it under the web3 gate; flagged in-code for re-scope at #4802.

Submission Checklist

  • Tests added or updated (happy path + at least one failure / edge case) per Testing Strategy
  • Diff coverage ≥ 80% — 81% locally (scoped cargo llvm-cov + diff-cover vs merge-base; a floor — boot-path lines in jsonrpc/init_stores are integration-covered in CI, not the scoped unit subset). Every new surface has unit tests (registration, dispatch, schema-lookup, tools, stores, subscribers, presets). CI ci-lite.yml gate is authoritative.
  • Coverage matrix updated — N/A: internal runtime-composition seam, no user-facing feature row
  • All affected feature IDs from the matrix are listed in the PR description under ## RelatedN/A: no matrix rows affected
  • No new external network dependencies introduced (mock backend used per Testing Strategy)
  • Manual smoke checklist updated if this touches release-cut surfaces — N/A: default full() path is byte-identical; desktop runtime unchanged
  • Linked issue closed via Closes #NNN in the ## Related section

Impact

  • Desktop / CLI / mobile / web: no behavior change. Default full() registration is byte-identical (asserted in test); examples/embed_headless.rs is the only caller opting into harness().
  • Performance: filter is an ambient-context group check; None ambient ⇒ no filter ⇒ no added work on the desktop path.
  • Security: a gated domain's controllers become unknown-method and its tools/stores/subscribers never initialize — narrows, never widens, the surface.
  • Migration: none. New axis is additive and defaults to the prior behavior.

Related


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

Linear Issue

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

Commit & Branch

  • Branch: feat/4796-domainset-filter-seam
  • Commit SHA: 49a872ce8271... (branch head; includes merge of upstream/main)

Validation Run

  • N/A: no app/ (frontend) changes — pnpm --filter openhuman-app format:check
  • N/A: no TypeScript changes — pnpm typecheck
  • Focused tests: core::all:: (49 passed, incl. full_registration_is_byte_identical, harness_excludes_gated_namespaces, dispatch_returns_none_for_gated_method, schema_lookup_is_gated_in_lockstep_with_dispatch), core::runtime::context::tests::store_init_plan_* (3), core::jsonrpc::tests::domain_subscriber_plan_* (3) + gated_method_is_unknown_at_transport_even_with_malformed_params, openhuman::tools::ops::tests::tool_group_* (2)
  • Rust fmt/check (if changed): cargo fmt --check clean · GGML_NATIVE=OFF cargo check exit 0 · cargo clippy --lib 0 errors, no new warnings in changed files · cargo check --example embed_headless exit 0
  • N/A: no app/src-tauri changes — Tauri fmt/check

Validation Blocked

  • command: cargo test --lib (full)
  • error: OOM on local machine (known; full lib suite exceeds local memory)
  • impact: full matrix runs in CI; locally scoped to changed domains (core::all, core::runtime, tools::ops) — all green

Behavior Changes

  • Intended behavior change: new opt-in DomainSet runtime axis; default full() unchanged
  • User-visible effect: none (desktop runtime byte-identical); enables slim harness embeds

Summary by CodeRabbit

  • New Features

    • Added runtime domain gating via a DomainSet axis (independent of background services) with full, harness, and none presets.
    • Dispatch, schema validation, store initialization, event subscribers, and available tools now respect the active DomainSet; gated methods behave like “unknown method.”
    • Updated the headless embedding example to explicitly select domains.
  • Documentation

    • Expanded guidance on DomainSet-based runtime composition and verification expectations.
  • Tests

    • Added coverage for DomainSet gating across dispatch, schema lookup, tool filtering, subscriber/store initialization, and updated a related e2e expectation.

oxoxDev added 7 commits July 13, 2026 13:24
…filter (tinyhumansai#4796)

Add a runtime DomainSet selector on CoreBuilder (sibling of ServiceSet) plus a
DomainGroup family enum. Every controller is tagged with its group once at the
single registration site via a GroupedController wrapper; the live surface
(controllers/schema/dispatch, workspace stores, event subscribers) is filtered
by the ambient CoreContext::domains(). full() is byte-identical to before;
harness() keeps only agent/memory/threads/config/security. Gated methods
dispatch as unknown (None); gated stores/subscribers are not initialized.

These files form one atomic seam — the init/bootstrap signature changes cycle
across all.rs, builder.rs, context.rs and jsonrpc.rs and only compile together.
…i#4796)

Post-filter all_tools_with_runtime by the ambient CoreContext DomainSet: tools
whose DomainGroup is disabled are dropped. Adds tool_group(name) classifying
each tool by its name() into its family (web3/mcp/skills/flows/media/voice/meet
gated; memory/threads mapped as harness-kept; everything else Platform). No
ambient context or full() keeps every tool.
…i#4796)

Build the headless embedder with the harness domain set so the example
exercises the runtime seam. core.version + openhuman.ping (a built-in alias)
stay valid regardless of the DomainSet.
…4796)

full_registration_is_byte_identical, harness_excludes_gated_namespaces,
dispatch_returns_none_for_gated_method, group_mapping_smoke (all_tests.rs);
tool_group classifier + harness/full retention (ops_tests.rs). Adapt the
validate_registry unit tests to the GroupedController signature via a grouped()
helper.
AGENTS.md: ServiceSet + DomainSet runtime composition note. phase-2 plan: 2.d
DomainSet section (Shape B filter seam; DomainRegistration struct collapse
remains deferred) + verification entries.
…nsai#4796)

Extract each workspace-store's DomainGroup guard into a pure StoreInitPlan
that init_stores consumes, giving the store->group mapping a single source
of truth. Assert full/none/harness plans directly — no process-global store
state, no runtime boot. Closes the DoD item-3 store-gating test gap.
…nyhumansai#4796)

Extract each event-bus subscriber's DomainGroup guard into a pure
DomainSubscriberPlan that register_domain_subscribers consumes, giving the
subscriber->group mapping a single source of truth. Assert full/none/harness
plans directly — no real subscribers, no process-global bus mutation. Closes
the DoD item-3 subscriber-gating test gap.
@oxoxDev
oxoxDev requested a review from a team July 13, 2026 09:03
@oxoxDev oxoxDev added this to the M0 — Feature-gate seam milestone Jul 13, 2026
@coderabbitai

coderabbitai Bot commented Jul 13, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 404cf57b-9eb6-49c8-b848-a32dbad323c2

📥 Commits

Reviewing files that changed from the base of the PR and between 49a872c and 32de700.

📒 Files selected for processing (4)
  • src/core/jsonrpc.rs
  • src/core/runtime/builder.rs
  • src/openhuman/tools/ops.rs
  • src/openhuman/tools/ops_tests.rs
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/openhuman/tools/ops.rs
  • src/core/runtime/builder.rs
  • src/core/jsonrpc.rs

📝 Walkthrough

Walkthrough

Adds DomainSet as a runtime composition axis alongside ServiceSet, propagates it through core initialization, and gates controller discovery/dispatch, stores, subscribers, and tools by DomainGroup.

Changes

DomainSet runtime composition

Layer / File(s) Summary
Runtime configuration and context contract
AGENTS.md, docs/plans/pluggable-core/..., examples/embed_headless.rs, src/core/runtime/..., src/lib.rs
Defines presets, builder/context propagation, exports, documentation, tests, and headless-example usage.
Grouped controller registration and dispatch
src/core/all.rs, src/core/all_tests.rs
Tags controllers with domain groups and filters discovery, schemas, and dispatch for disabled groups.
Store and subscriber initialization gates
src/core/runtime/context.rs, src/core/jsonrpc.rs, src/core/jsonrpc_tests.rs
Conditionally initializes stores, subscribers, payment-ledger setup, and workflow subscriptions.
Runtime tool filtering
src/openhuman/tools/ops.rs, src/openhuman/tools/ops_tests.rs
Classifies tools by domain family and filters them using the active DomainSet.
Retrieval compatibility validation
tests/agent_retrieval_e2e.rs
Keeps memory_recall allowed in orchestrator tool expectations.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CoreBuilder
  participant CoreContext
  participant ControllerRegistry
  participant ToolRegistry
  participant RuntimeBootstrap
  CoreBuilder->>CoreContext: configure DomainSet
  CoreContext->>ControllerRegistry: expose allowed controllers and schemas
  CoreContext->>ToolRegistry: filter tools by DomainGroup
  CoreContext->>RuntimeBootstrap: initialize selected stores and subscribers
Loading

Possibly related issues

  • Issue 4821 — Proposes replacing heuristic tool classification with per-tool DomainGroup metadata.
  • Issue 4795 — Covers the broader DomainSet runtime-gating work implemented here.

Possibly related PRs

Suggested labels: feature, rust-core

Poem

I’m a rabbit with domains in my hat,
Harness hops where the full set sat.
Controllers hide, tools tidy their lair,
Stores wake only when called there.
CoreBuilder knows the path to share.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The agent_retrieval_e2e memory_recall expectation tweak is unrelated to the DomainSet gating scope. Move the memory_recall expectation adjustment to a separate PR, or revert it here if it is not part of #4796.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Specific and aligned with the main change: DomainSet runtime composition with group-tagged filtering for #4796.
Linked Issues check ✅ Passed It adds DomainSet presets, domain filtering for controllers/tools/stores/subscribers, unknown-method gating, docs, and tests as requested.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

@coderabbitai coderabbitai Bot added feature Net-new user-facing capability or product behavior. rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure. labels Jul 13, 2026

@chatgpt-codex-connector chatgpt-codex-connector 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.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 70ca2c32ae

ℹ️ 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".

Comment thread src/core/all.rs
Comment on lines 1067 to +1071
registry()
.iter()
.chain(internal_registry().iter())
.find(|r| r.rpc_method_name() == method)
.map(|r| r.schema.clone())
.find(|g| g.controller.rpc_method_name() == method)
.map(|g| g.controller.schema.clone())

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Filter gated methods before validating params

When an ambient DomainSet::harness()/none() is active, invoke_method_inner still uses this unfiltered schema lookup before try_invoke_registered_rpc enforces the domain gate. A disabled method such as openhuman.flows_* with missing or extra parameters will return the controller's validation error instead of method-not-found, which violates the documented contract that gated controllers are indistinguishable from absent and leaks the hidden RPC surface. Filter this lookup by group_allowed or skip validation for gated groups.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Addressed in 7230922schema_for_rpc_method now filters by group_allowed exactly as try_invoke_registered_rpc does, so a gated method returns method-not-found at both validation and dispatch (no param-error leak). Proven by schema_lookup_is_gated_in_lockstep_with_dispatch + a transport-level test through invoke_method.

Comment on lines +1193 to +1195
// Everything else — shell/file/screen/config/security/agent/billing/… — is
// Platform: present under full(), absent under harness()/none().
DomainGroup::Platform

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Classify gate-domain tools before Platform fallback

For custom DomainSets that disable a gate family while leaving platform enabled, any existing tool not matched above falls through here as Platform. Current gate-domain tools such as audio_generate_podcast/audio_email_podcast (Voice) and whatsapp_data_* (Channels) are not classified, so agents can still call them even though the corresponding controllers/schema are gated off. Add classifications for the existing non-Platform tool names before this fallback.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Addressed in 9f8f558 — added an explicit VOICE list (audio_generate_podcast / audio_email_podcast / audio_generate_and_email_podcast) and a whatsapp_data_ → Channels prefix, so these gate-family tools no longer fall through to Platform. Tests updated.

Comment thread src/core/jsonrpc.rs Outdated
Comment on lines 1862 to 1863
static REGISTERED: Once = Once::new();
REGISTERED.call_once(|| {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Do not let the first DomainSet suppress subscribers forever

Because this registration is guarded by a single process-wide Once, the first runtime to call it fixes the subscriber set for the entire process. If an embedder or test builds DomainSet::harness()/none() first and later widens to DomainSet::full(), the channels/flows/MCP/platform subscribers skipped on the first call will never be installed, even though those domains' controllers are now exposed. Make subscriber registration idempotent per group or allow later calls to add newly enabled subscribers.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Addressed in b40a06f — replaced the single process-wide Once with a per-DomainGroup registered-set: ungated infra runs once, and each gated group installs the first time it is enabled, so widening harness()full() later installs the newly-enabled subscribers. DomainGroup now derives Hash for the set.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/core/jsonrpc.rs (2)

2082-2084: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Make the completion diagnostic reflect the actual registration plan.

The message claims webhook, channel, agent, and MCP subscribers were registered even when their branches were skipped.

Proposed diagnostic fix
 log::info!(
-    "[event_bus] domain subscribers registered (webhook, channel, health, conversation, composio, restart, proactive, agent, session_expired, mcp_client)"
+    "[event_bus] domain subscriber registration complete: plan={plan:?}"
 );

As per coding guidelines, new or changed flows require grep-friendly diagnostics for branches, state transitions, and exit outcomes.

🤖 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/core/jsonrpc.rs` around lines 2082 - 2084, Update the completion
diagnostic in the domain subscriber registration flow near the existing
log::info call to reflect only subscribers actually registered, rather than
listing all possible subscriber types unconditionally. Track or construct the
registered subscriber names as each conditional branch succeeds, then emit that
collection in the final diagnostic while preserving grep-friendly branch and
completion logging.

Source: Coding guidelines


2096-2121: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Apply DomainSet to domain initialization outside the subscriber registrar.

The new argument is only used by register_domain_subscribers. Lines 2187 and 2247 still initialize the Web3 x402 ledger and Skills triggered-workflow subscriber under harness() and none(). Gate both by their owning DomainGroup and log skipped branches.

This conflicts with the PR objective that excluded stores and subscribers remain uninitialized.

🤖 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/core/jsonrpc.rs` around lines 2096 - 2121, Update bootstrap_core_runtime
so DomainSet controls domain-owned initialization outside
register_domain_subscribers. In the harness() path, initialize the Web3 x402
ledger only when its owning DomainGroup is enabled; in the none() path,
initialize the Skills triggered-workflow subscriber only when its owning
DomainGroup is enabled. Log each skipped initialization branch, ensuring
excluded stores and subscribers remain uninitialized.
🧹 Nitpick comments (1)
src/core/all.rs (1)

1219-1235: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add complete, grep-friendly diagnostics to both DomainSet gating flows.

  • src/core/all.rs#L1219-L1235: log lookup misses, allowed dispatch, and completion/error in addition to suppression.
  • src/openhuman/tools/ops.rs#L1054-L1075: log the selected DomainSet, bypass branch, and before/after counts.

As per coding guidelines, “New or changed flows must include verbose, grep-friendly diagnostics for 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/core/all.rs` around lines 1219 - 1235, Expand diagnostics for both
DomainSet gating flows: in src/core/all.rs lines 1219-1235, update the method
lookup/dispatch flow around registry().iter(), internal_registry(), and
group_allowed to log lookup misses, allowed dispatch, completion, and errors in
addition to suppression; in src/openhuman/tools/ops.rs lines 1054-1075, update
the corresponding DomainSet handling to log the selected DomainSet, bypass
branch, and before/after counts. Use consistent verbose, grep-friendly messages
for each branch and state transition.

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/core/all_tests.rs`:
- Around line 660-684: Replace the self-referential registry comparison in
full_registration_is_byte_identical with an independently checked-in, ordered
pre-change RPC method snapshot, and assert the unfiltered
all_registered_controllers() output matches it in order and length. In
docs/plans/pluggable-core/phase-2-corecontext.md at line 153, mark the
verification complete only after this independent assertion is implemented.
- Around line 735-764: Extend dispatch_returns_none_for_gated_method to exercise
the JSON-RPC handler rather than only try_invoke_registered_rpc: send both valid
and malformed-parameter requests for the gated flows method through the
transport-level handler, and assert each returns the unknown-method error.
Retain the existing direct-dispatch and security.policy_info checks as needed,
ensuring the test proves gated methods are rejected after JSON-RPC schema
validation as well.

In `@src/core/runtime/builder.rs`:
- Around line 120-123: Update the documentation describing DomainSet::none to
clarify that it disables all optional domain families, not the entire runtime;
explicitly note that transport built-ins and always-on core infrastructure
remain active. Apply the same clarification to the corresponding repeated
documentation block.

In `@src/openhuman/tools/ops.rs`:
- Around line 1079-1195: Replace name-based classification in tool_group with
authoritative explicit DomainGroup metadata on every tool, ensuring Agent,
Config, Security, and Channels tools are correctly grouped without
disabled-family leakage. Update AGENTS.md at line 217 and
docs/plans/pluggable-core/phase-2-corecontext.md lines 135-140 to describe the
authoritative grouping mechanism. In src/core/all.rs lines 682-689, assign
TokenJuice to an ungated/core group; in src/core/all_tests.rs lines 687-733,
assert it remains visible under harness(); and in
src/openhuman/tools/ops_tests.rs lines 2197-2228, add coverage for Agent,
Config, Security, Channels, and TokenJuice classification.

---

Outside diff comments:
In `@src/core/jsonrpc.rs`:
- Around line 2082-2084: Update the completion diagnostic in the domain
subscriber registration flow near the existing log::info call to reflect only
subscribers actually registered, rather than listing all possible subscriber
types unconditionally. Track or construct the registered subscriber names as
each conditional branch succeeds, then emit that collection in the final
diagnostic while preserving grep-friendly branch and completion logging.
- Around line 2096-2121: Update bootstrap_core_runtime so DomainSet controls
domain-owned initialization outside register_domain_subscribers. In the
harness() path, initialize the Web3 x402 ledger only when its owning DomainGroup
is enabled; in the none() path, initialize the Skills triggered-workflow
subscriber only when its owning DomainGroup is enabled. Log each skipped
initialization branch, ensuring excluded stores and subscribers remain
uninitialized.

---

Nitpick comments:
In `@src/core/all.rs`:
- Around line 1219-1235: Expand diagnostics for both DomainSet gating flows: in
src/core/all.rs lines 1219-1235, update the method lookup/dispatch flow around
registry().iter(), internal_registry(), and group_allowed to log lookup misses,
allowed dispatch, completion, and errors in addition to suppression; in
src/openhuman/tools/ops.rs lines 1054-1075, update the corresponding DomainSet
handling to log the selected DomainSet, bypass branch, and before/after counts.
Use consistent verbose, grep-friendly messages for each branch and state
transition.
🪄 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: 075e5cb0-6730-4fc1-9fec-84e18faa50e6

📥 Commits

Reviewing files that changed from the base of the PR and between c58fe10 and 70ca2c3.

📒 Files selected for processing (13)
  • AGENTS.md
  • docs/plans/pluggable-core/phase-2-corecontext.md
  • examples/embed_headless.rs
  • src/core/all.rs
  • src/core/all_tests.rs
  • src/core/jsonrpc.rs
  • src/core/jsonrpc_tests.rs
  • src/core/runtime/builder.rs
  • src/core/runtime/context.rs
  • src/core/runtime/mod.rs
  • src/lib.rs
  • src/openhuman/tools/ops.rs
  • src/openhuman/tools/ops_tests.rs

Comment thread src/core/all_tests.rs
Comment thread src/core/all_tests.rs
Comment thread src/core/runtime/builder.rs Outdated
Comment thread src/openhuman/tools/ops.rs
oxoxDev added 5 commits July 13, 2026 15:47
…humansai#4796)

Codex review (tinyhumansai#4808): invoke_method_inner validated params against the FULL
registry before the dispatch gate fired, so a gated method (e.g.
openhuman.flows_*) with bad params returned the controller's validation error
instead of method-not-found — leaking the hidden RPC surface and violating the
'gated == absent' contract. Filter schema_for_rpc_method by group_allowed
exactly as try_invoke_registered_rpc does. Also strengthen the byte-identical
test to assert full() registration ORDER (not just membership) per CodeRabbit,
and derive Hash on DomainGroup for the subscriber-registry set.
…nyhumansai#4796)

Codex review (tinyhumansai#4808): the tool_group classifier mapped Voice via voice_/tts_/
stt_ prefixes and had no Channels arm, so audio_generate_podcast /
audio_email_podcast / audio_generate_and_email_podcast (Voice) and
whatsapp_data_* (Channels) fell through to Platform — leaving them callable
under a DomainSet that gates those families off while keeping Platform. Add an
explicit VOICE list + a whatsapp_data_ Channels prefix.
…inyhumansai#4796)

Codex review (tinyhumansai#4808): the single process-wide Once in register_domain_subscribers
fixed the subscriber set to the FIRST caller's DomainSet — widening harness()->
full() later never installed the skipped subscribers. Split ungated infra into
its own Once and track registered DomainGroups so a wider DomainSet installs
exactly the newly-enabled groups. Fix the completion log to report the actual
plan (CodeRabbit). Also gate the x402 payment ledger (Web3) and the
triggered-workflow subscriber (Skills) in bootstrap_core_runtime — both still
initialized under harness()/none() (CodeRabbit).
…inyhumansai#4796)

CodeRabbit review (tinyhumansai#4808): 'nothing' / 'disables everything' misstated the
contract — transport built-ins and always-on core infra still run under none().
tinyhumansai#4796)

CodeRabbit review (tinyhumansai#4808): the direct-dispatch + schema-lookup tests don't
exercise invoke_method (the transport path where validate-before-gate leaked).
Send a gated flows method with both well-formed AND malformed params through
invoke_method under harness() and assert unknown-method (never a param error).

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/core/all.rs (1)

1213-1241: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Guard RPC-name uniqueness across both registries. validate_registry() only checks the build_registered_controllers() slice; internal_registry() is never included in the duplicate-RPC check. Since both schema_for_rpc_method() and try_invoke_registered_rpc() search the combined registry() + internal_registry() set, an overlapping RPC name could still split schema lookup from dispatch.

🤖 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/core/all.rs` around lines 1213 - 1241, Update validate_registry() to
include entries from internal_registry() when checking RPC-name uniqueness,
matching the combined lookup used by schema_for_rpc_method() and
try_invoke_registered_rpc(). Reject duplicate rpc_method_name() values across
both registries while preserving the existing validation behavior for unique
names.
🧹 Nitpick comments (1)
src/core/all.rs (1)

1063-1077: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a grep-friendly diagnostic for the gate branch.

try_invoke_registered_rpc logs (log::debug!) when it suppresses a call due to group_allowed returning false, but schema_for_rpc_method's identical gate has no logging at all — despite its own comment stating it filters "identically to try_invoke_registered_rpc". This makes it harder to grep logs for schema-lookup suppressions specifically (e.g. to debug a hidden-method report).

🔍 Proposed diagnostic
 pub fn schema_for_rpc_method(method: &str) -> Option<ControllerSchema> {
     registry()
         .iter()
         .chain(internal_registry().iter())
-        .find(|g| g.controller.rpc_method_name() == method && group_allowed(g.group))
-        .map(|g| g.controller.schema.clone())
+        .find(|g| g.controller.rpc_method_name() == method)
+        .filter(|g| {
+            let allowed = group_allowed(g.group);
+            if !allowed {
+                log::debug!(
+                    "[rpc][domain-gate] schema lookup for '{method}' suppressed — group {:?} disabled under active DomainSet",
+                    g.group
+                );
+            }
+            allowed
+        })
+        .map(|g| g.controller.schema.clone())
 }

As per path instructions, "New or changed flows must include verbose, grep-friendly diagnostics for entry/exit, branches, external calls, retries/timeouts, state transitions, and errors" and "Use log or tracing at debug or trace levels for Rust diagnostics."

🤖 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/core/all.rs` around lines 1063 - 1077, Update schema_for_rpc_method to
add a log::debug diagnostic when a matching method is rejected because
group_allowed returns false, using a grep-friendly message that clearly
identifies schema lookup suppression and the RPC method. Keep allowed matches
and genuinely unregistered methods on their existing behavior.

Source: Path instructions

🤖 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.

Outside diff comments:
In `@src/core/all.rs`:
- Around line 1213-1241: Update validate_registry() to include entries from
internal_registry() when checking RPC-name uniqueness, matching the combined
lookup used by schema_for_rpc_method() and try_invoke_registered_rpc(). Reject
duplicate rpc_method_name() values across both registries while preserving the
existing validation behavior for unique names.

---

Nitpick comments:
In `@src/core/all.rs`:
- Around line 1063-1077: Update schema_for_rpc_method to add a log::debug
diagnostic when a matching method is rejected because group_allowed returns
false, using a grep-friendly message that clearly identifies schema lookup
suppression and the RPC method. Keep allowed matches and genuinely unregistered
methods on their existing behavior.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e8f58f27-0ddc-41a8-8b61-0c40cb61d092

📥 Commits

Reviewing files that changed from the base of the PR and between 70ca2c3 and 5243895.

📒 Files selected for processing (7)
  • src/core/all.rs
  • src/core/all_tests.rs
  • src/core/jsonrpc.rs
  • src/core/jsonrpc_tests.rs
  • src/core/runtime/builder.rs
  • src/openhuman/tools/ops.rs
  • src/openhuman/tools/ops_tests.rs
🚧 Files skipped from review as they are similar to previous changes (5)
  • src/openhuman/tools/ops_tests.rs
  • src/core/runtime/builder.rs
  • src/core/jsonrpc.rs
  • src/openhuman/tools/ops.rs
  • src/core/all_tests.rs

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 13, 2026
oxoxDev added 2 commits July 13, 2026 17:05
…ansai#4764

Pre-existing main breakage surfaced by this PR's coverage run (unrelated to the
DomainSet seam): tinyhumansai#4764 (383581b) gave the orchestrator direct memory_recall/
memory_store tools, but agent_retrieval_e2e still asserted memory_recall must be
absent, so orchestrator_reaches_memory_agent_on_demand failed on main. Remove
memory_recall from the superseded-tools list; the deep tree-walk tools stay
delegated behind agent_memory.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/agent_retrieval_e2e.rs (1)

189-215: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Assert that the retained memory tools are present.

Removing memory_recall from the negative list prevents a false failure, but this test can now pass even if memory_recall and memory_store are accidentally absent. Add positive assertions for the retained first-class tools so the compatibility contract is covered.

🤖 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 `@tests/agent_retrieval_e2e.rs` around lines 189 - 215, Extend the test around
the superseded-tool loop to positively assert that the orchestrator
configuration contains both retained first-class tools, “memory_recall” and
“memory_store”. Reuse the existing TOML line-matching approach and fail with a
clear assertion if either tool is absent, while preserving the current negative
assertions for superseded tools.
🤖 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.

Outside diff comments:
In `@tests/agent_retrieval_e2e.rs`:
- Around line 189-215: Extend the test around the superseded-tool loop to
positively assert that the orchestrator configuration contains both retained
first-class tools, “memory_recall” and “memory_store”. Reuse the existing TOML
line-matching approach and fail with a clear assertion if either tool is absent,
while preserving the current negative assertions for superseded tools.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e699fc22-5af2-4716-9481-5b45afe6ee55

📥 Commits

Reviewing files that changed from the base of the PR and between 5243895 and 49a872c.

📒 Files selected for processing (1)
  • tests/agent_retrieval_e2e.rs

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 13, 2026

@M3gA-Mind M3gA-Mind left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

PR #4808 — feat(core): runtime DomainSet composition axis

Walkthrough

Adds DomainSet as a second runtime-composition axis on CoreBuilder (sibling to ServiceSet) that selects which domain families are live. Every controller is tagged with a DomainGroup once, at the single registration site (build_registered_controllers / build_internal_only_controllers) via a GroupedController wrapper, and five surfaces — /schema, dispatch, agent tools, workspace stores, and event-bus subscribers — filter by the ambient CoreContext::domains(). The default full() preset is a byte-identical no-op for desktop/CLI; harness() and none() are the slim opt-ins.

Overall this is a clean, well-tested seam. The design is sound: the ambient CoreContext::current() (task-local → DEFAULT_CONTEXT fallback) means the filter Just Works for both scoped multi-tenant dispatch and the headless boot path, and the gate is enforced at both schema-lookup and dispatch so a gated method can never leak a param-validation error (verified by gated_method_is_unknown_at_transport_even_with_malformed_params). All four CodeRabbit findings and the three Codex findings were addressed thoughtfully. My independent pass found no blockers — a couple of doc/consistency nits and one design question about the harness tool surface. Leaving this as a comment; approval is the maintainer's call.

Changes

File Summary
AGENTS.md Documents the ServiceSet + DomainSet two-axis runtime composition.
docs/plans/pluggable-core/phase-2-corecontext.md Adds §2.d describing the filter-seam mechanism + verification tests.
examples/embed_headless.rs Switches the embed example to DomainSet::harness().
src/core/all.rs Adds DomainGroup, GroupedController, push, group_allowed; tags every controller; filters all_registered_controllers/all_controller_schemas/schema_for_rpc_method/try_invoke_registered_rpc.
src/core/all_tests.rs Byte-identical-full, harness-exclusion, dispatch-gate, schema-lockstep, group-mapping tests.
src/core/jsonrpc.rs DomainSubscriberPlan; per-DomainGroup idempotent subscriber registration; gates x402 ledger + skills subscriber.
src/core/jsonrpc_tests.rs Subscriber-plan tests + transport-level malformed-param gate test.
src/core/runtime/builder.rs DomainSet struct + full/harness/none presets + allows; threads .domains() through CoreBuilder.
src/core/runtime/context.rs Stores domains on CoreContext; StoreInitPlan; gates init_stores; for_test ctor.
src/core/runtime/mod.rs, src/lib.rs Re-export DomainSet.
src/openhuman/tools/ops.rs tool_group name classifier + ambient post-filter in all_tools_with_runtime.
src/openhuman/tools/ops_tests.rs tool_group classification + harness-drop tests.
tests/agent_retrieval_e2e.rs Drops stale memory_recall negative assertion (unrelated #4764 fix, called out in PR body).

Actionable comments (3)

💡 Refactor / suggestion

1. src/core/jsonrpc.rs:1805-1817 — Orphaned doc comment now mis-documents DomainSubscriberPlan

The two leading lines ("Registers all long-lived domain event-bus subscribers exactly once. / Guarded by std::sync::Once so repeated calls to bootstrap_core_runtime are safe and idempotent.") were the original doc for register_domain_subscribers. Because the new DomainSubscriberPlan struct doc was inserted directly after them without a blank line, they are now a single contiguous /// block attached to DomainSubscriberPlan — a pure decision struct that registers nothing and is not Once-guarded. The Once claim is also stale: the single Once was replaced by INFRA: Once + the per-group group_first_time set.

Suggested change:

// move these two lines down onto register_domain_subscribers, and correct the guard wording:
/// Registers all long-lived domain event-bus subscribers, each group at most
/// once per process. Ungated core infra runs once behind `INFRA: Once`; each
/// gated `DomainGroup` installs the first time it is enabled (`group_first_time`),
/// so widening `harness()` → `full()` later still installs the newly-enabled groups.
fn register_domain_subscribers(...) { ... }

/// Per-`DomainGroup` gating decision for each event-bus subscriber ...
pub struct DomainSubscriberPlan { ... }

❓ Question

2. src/openhuman/tools/ops.rs:1091-1213harness() keeps Agent/Config/Security controllers live but drops their agent tools

Controller gating and tool gating use different groupings for the same domain. Under harness() the Agent/Config/Security controllers dispatch fine over RPC, but the corresponding agent tools (spawn_subagent, config/security tools, all agent-orchestration tools) fall through tool_group to Platform and are dropped. So the "embeddable agent core" agent surface is effectively memory + threads/todos only — it can't spawn sub-agents or read config even though those RPCs exist. That's a defensible strict-minimal choice for a seam PR (and you documented it), but it's worth confirming it's intended, since the doc string ("embeddable agent core") reads as more capable than the resulting tool surface. Consider a follow-up tracking issue for the per-Tool DomainGroup metadata so the two axes converge.

There's also no guard test that the tool classifier stays in sync with reality: a newly-added Web3/Voice/Meet tool that doesn't match a listed name/prefix silently becomes Platform, which would leak it under a custom DomainSet{ platform: true, web3: false }. A cheap regression guard:

#[test]
fn no_gate_family_tool_silently_defaults_to_platform() {
    // Every tool whose name starts with a known gate prefix must NOT map to Platform.
    for name in ["wallet_x", "web3_x", "x402_x", "mcp_x", "media_x"] {
        assert_ne!(tool_group(name), DomainGroup::Platform, "{name} must not default to Platform");
    }
}

💡 Refactor / suggestion

3. src/core/runtime/builder.rs:157 / src/core/all.rsDomainGroup::Media has zero tagged controllers

No controller in build_registered_controllers is tagged Media (grep confirms no media namespace); Media is referenced only by the media_generate_* tools in tool_group. So DomainSet.media gates tools but no controller/store/subscriber. That's harmless today but is a latent asymmetry (a custom set with media:false, platform:true drops the media tools while their backing controllers stay live). Worth a one-line comment on the media field noting it currently gates only the tool surface, or fold the media generation controller (when it lands) into the group.

Nitpicks (2)

  • src/core/jsonrpc.rs:2137-2138 — the bootstrap_core_runtime comment "Uses a Once guard so repeated calls … cannot double-subscribe" is now technically served by the per-group set rather than a function-level Once; the guarantee holds but the wording predates the refactor.
  • src/core/all.rs:1058 (rpc_method_from_parts) stays intentionally unfiltered (documented). It backs cli.rs:436, so under harness() the local CLI can still resolve/print help for a gated method's schema — a mild surface leak, but CLI is a trusted local operator surface and this is out of scope for the seam. Fine to leave; flagging for awareness.

Verified / looks good

  • Ambient filter is correct on both paths: CoreContext::current() falls back to DEFAULT_CONTEXT, so /schema + dispatch outside an explicit scope still filter by the runtime's DomainSet (harness embeds) while desktop full() is a true no-op.
  • Gate enforced at both schema_for_rpc_method and try_invoke_registered_rpc, so a gated method with malformed params returns unknown-method, never a param-validation leak — proven at the transport layer.
  • Per-group group_first_time correctly supports widening (harness()full() installs newly-enabled groups; already-registered groups are not double-subscribed).
  • full_registration_is_byte_identical asserts order + membership against the raw registry (post sort-removal) — the right property for the filter-identity claim.
  • StoreInitPlan / DomainSubscriberPlan extracted as pure values with direct unit tests — no process-global mutation needed to verify gating. Good pattern.
  • Convention-compliant: no new root-level .rs, single registration site preserved, mod.rs untouched, DomainSet/DomainGroup live in the transport/registry layer, AGENTS.md + plan docs updated.

Comment thread src/core/jsonrpc.rs Outdated
@@ -1806,81 +1806,105 @@ async fn run_server_with_services(
///
/// Guarded by `std::sync::Once` so repeated calls to `bootstrap_core_runtime`

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Orphaned doc comment (minor). These two lines — "Registers all long-lived domain event-bus subscribers exactly once" / "Guarded by std::sync::Once…" — were the doc for register_domain_subscribers, but with no blank line before the new struct doc they now form one contiguous /// block attached to DomainSubscriberPlan, which registers nothing and isn't Once-guarded. The Once claim is also stale (replaced by INFRA: Once + the per-group group_first_time set). Move these two lines down onto register_domain_subscribers and correct the guard wording to describe the per-group idempotency.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Addressed in 32de700 — moved the "registers subscribers … idempotent" doc back onto register_domain_subscribers and rewrote it for the real mechanism (ungated infra once behind INFRA: Once; each DomainGroup installs the first time it's enabled via group_first_time). Also fixed the stale Once wording at the bootstrap_core_runtime call site (nitpick 1).

/// only memory + thread/todo tools remain. This is the strict #4796 harness
/// surface; an embedder that wants a broader tool set can widen its DomainSet.
/// (Names verified against each Tool impl's `fn name()` on 2026-07-13.)
fn tool_group(name: &str) -> crate::core::all::DomainGroup {

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Harness keeps Agent/Config/Security controllers but drops their tools (question + guard-test suggestion). Controller gating and tool gating use different groupings here: under harness() the Agent/Config/Security controllers dispatch over RPC, but spawn_subagent, config/security, and all agent-orchestration tools fall through to Platform and are dropped — so the agent tool surface is effectively memory + threads only. Defensible as a strict minimal seam (and documented), but the "embeddable agent core" wording oversells the resulting agent capability; worth confirming intent + a follow-up for per-Tool DomainGroup metadata so the axes converge.\n\nSeparately, the name-based classifier has no sync guard: a future gate-family tool that matches no listed name/prefix silently becomes Platform and would leak under a custom DomainSet{ platform:true, web3:false }. Cheap regression:\n\nrust\n#[test]\nfn no_gate_family_tool_silently_defaults_to_platform() {\n for name in ["wallet_x","web3_x","x402_x","mcp_x","media_x"] {\n assert_ne!(tool_group(name), DomainGroup::Platform);\n }\n}\n

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Addressed in d06e24b. Guard test no_gate_family_tool_silently_defaults_to_platform added; and to actually close the leak I switched the prefix-owning gate families to prefix matching (Web3 = wallet_/web3_/x402_, Media = media_) so a new family tool auto-gates instead of exact-list-missing → Platform.

On the design question: the strict-minimal harness tool surface (memory + threads only; Agent/Config/Security tools drop to Platform) is intentional for this seam PR and documented. You're right the two axes should converge — opened #4821 to give every Tool authoritative DomainGroup metadata so tool gating matches controller gating (and the name heuristic + this guard can then be removed).

/// Speech-to-text / text-to-speech, audio toolkit.
pub voice: bool,
/// Image/video media generation.
pub media: bool,

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

DomainGroup::Media gates only tools, no controllers (nit). No controller in build_registered_controllers is tagged Media (no media namespace exists); Media is referenced only by the media_generate_* tools in tool_group. So DomainSet.media gates the tool surface but no controller/store/subscriber — a custom set with media:false, platform:true would drop the media tools while their backing controllers stay live. Harmless today; worth a one-line comment on this field noting it currently gates only the tool surface.

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Addressed in 32de700 — added a note on the DomainSet::media field that it currently gates only the media_generate_* tool surface (no Media controller/store/subscriber yet), and to fold the media-generation controller into the group when it lands. Convergence tracked in #4821.

@M3gA-Mind

Copy link
Copy Markdown
Collaborator

Review summary — DomainSet composition seam

Reviewed the full diff (registry/filter design, correctness, edge cases, security, repo conventions) and re-read the existing CodeRabbit + Codex threads. Left as a COMMENT review — approval is the maintainer's call.

Verdict: looks-good / non-blocking. No blockers, no majors.

Verified correct (checked against code, not just the reply threads)

  • Fail-safe filter. CoreContext::current() falls back to DEFAULT_CONTEXT (bound to the built DomainSet at init()), so /schema + dispatch filter correctly even outside an explicit scope, and desktop full() is a true no-op (byte-identical proven by full_registration_is_byte_identical).
  • No param-validation leak. The gate is enforced at both schema_for_rpc_method and try_invoke_registered_rpc, so a gated method with malformed params returns unknown-method — confirmed at the transport layer by gated_method_is_unknown_at_transport_even_with_malformed_params.
  • Widening works. The per-group group_first_time set installs newly-enabled subscribers on a later harness()full() call without double-subscribing (correctly replaces the old process-wide Once).
  • All three Codex P2 findings and the CodeRabbit threads are genuinely addressed in-code; the author additionally gated the x402 ledger (Web3) and skills trigger subscriber (Skills) in bootstrap_core_runtime.
  • Convention-compliant: single registration site preserved (group tag compiler-forced via push()), no new root-level .rs, mod.rs untouched, docs/AGENTS.md updated.

Non-blocking items (3 inline comments)

  • src/core/jsonrpc.rs:1807 — the register_domain_subscribers doc comment got orphaned onto DomainSubscriberPlan; the "Once-guarded" wording is now stale.
  • src/openhuman/tools/ops.rs — name-based tool_group() is a hand-maintained list with no drift guard; a future gate-family tool would default to Platform and could leak under a custom DomainSet (platform-on / gate-off). Author already deferred the per-Tool metadata fix (CodeRabbit accepted); a small sync-guard test would de-risk.
  • src/core/runtime/builder.rs — the Media group currently tags zero controllers (tools-only gate) — worth a note.

One question for the maintainer

Under harness() the Agent/Config/Security controllers stay live, but their agent tools are dropped (anything unclassified defaults to Platform, which harness() disables) — so the embeddable "agent core" tool surface is effectively memory + threads only (no shell/file/spawn_subagent). Defensible and documented, but worth confirming that's the intended embed surface.

Note: this PR also bundles one unrelated pre-existing test fix (agent_retrieval_e2e.rs, superseded by #4764) — disclosed by the author, CI green.

oxoxDev added 2 commits July 13, 2026 18:56
…ult (tinyhumansai#4796)

Maintainer review (tinyhumansai#4808): the name-based tool classifier used exact-name lists
for Web3/Media, so a future tool in those families (no matching name) would
silently fall through to Platform and leak under a custom DomainSet. Switch
Web3 (wallet_/web3_/x402_) and Media (media_) to domain-exclusive prefixes, and
add no_gate_family_tool_silently_defaults_to_platform as a regression guard.
Axis-convergence via per-Tool metadata tracked in tinyhumansai#4821.
…inyhumansai#4796)

Maintainer review (tinyhumansai#4808): (1) the original register_domain_subscribers doc
("registered exactly once / Once-guarded") had orphaned onto DomainSubscriberPlan
with a stale Once claim — move it back to the fn and describe the per-group
idempotency (INFRA: Once + group_first_time). (2) Update the bootstrap call-site
comment likewise. (3) Note on DomainSet::media that it currently gates only the
media_generate_* tool surface (no Media controller/store/subscriber yet).
@oxoxDev

oxoxDev commented Jul 13, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks for the thorough pass @M3gA-Mind — all 3 actionable items + nitpick 1 pushed in d06e24b/32de70009. On nitpick 2 (rpc_method_from_parts stays unfiltered → local CLI can resolve a gated method's help): leaving as-is per your call — it's a trusted local-operator surface, documented, and out of scope for the seam. Axis-convergence follow-up: #4821.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jul 13, 2026
@M3gA-Mind

Copy link
Copy Markdown
Collaborator

Re-review — commits d06e24bf7 + 32de70009

Re-reviewed the two follow-up commits. All three of my inline findings are resolved correctly:

  • tool_group drift guard — prefix-gating for the domain-exclusive families (wallet_/web3_/x402_ → Web3, media_ → Media, mcp_ → Mcp) plus the new no_gate_family_tool_silently_defaults_to_platform test. Nice side effect: the prefix switch now also catches six wallet_* tools the old exact list had missed (wallet_balances, wallet_execute_prepared, wallet_supported_assets, wallet_network_defaults, wallet_prepared_quotes, wallet_encode_erc20_transfer) — those would previously have defaulted to Platform and leaked under a { web3: false, platform: true } set. Good catch beyond the original ask.
  • Orphaned doc comment — fixed; register_domain_subscribers now carries an accurate per-group doc and the stale "Once-guarded" wording is gone at both the fn and the call site.
  • Media group — documented as a tools-only gate with a clear TODO to fold in the backing controller when a media namespace lands. Reasonable.

Residual (already documented in-code, non-blocking): Skills/Flows keep exact-list matching since they have no clean prefix, so a future skills/flows tool would still default to Platform — only matters under an exotic custom DomainSet (harness() has both + platform off anyway).

⚠️ One thing blocks merge — a rebase, not a code issue

The branch is now CONFLICTING. The conflict is entirely in the bundled unrelated change to tests/agent_retrieval_e2e.rs: main independently landed a superset of that fix (it removes memory_recall from the forbidden list and adds positive assertions that memory_recall/memory_store/save_preference/update_memory_md are present as direct orchestrator tools). Your bundled hunk only did the removal, so it collides.

Resolution is trivial: when merging main, take main's version of that hunk — your bundled change is fully subsumed by it. (This is the downside of bundling the unrelated fix; it's now redundant.)

Otherwise the seam itself is unchanged and still looks good. CI is re-running on the new head (Rust Quality / CodeRabbit / Submission checklist pending at time of writing). Leaving as a comment — approval remains the maintainer's call.

…-filter-seam

# Conflicts:
#	tests/agent_retrieval_e2e.rs
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature Net-new user-facing capability or product behavior. rust-core Core Rust runtime in src/: CLI, core_server, shared infrastructure.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(core): DomainSet on CoreBuilder + DomainRegistration filter seam (feature-gate prerequisite)

3 participants