Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
ec598bd
fix(tauri): drop needless returns in macos window command tails (#5018)
oxoxDev Jul 17, 2026
783416a
fix(tauri): drop needless return in macos claude-login launch (#5018)
oxoxDev Jul 17, 2026
07038ca
fix(tauri): drop needless returns in macos notification permission co…
oxoxDev Jul 17, 2026
aa06e51
fix(tauri): use slice contains over iter().any() in imessage scanner …
oxoxDev Jul 17, 2026
100d343
fix(tauri): allow dead RAII webview field and drop redundant deref in…
oxoxDev Jul 17, 2026
a35bb78
fix(tauri): remove unused is_open and drop redundant deref in notch w…
oxoxDev Jul 17, 2026
9d10f98
refactor(core): relocate web chat conduit to web_chat/ (#5002)
oxoxDev Jul 16, 2026
f959e64
refactor(channels): point intra-channels call sites at web_chat (#5002)
oxoxDev Jul 16, 2026
8a92e9d
refactor(core): point external call sites at web_chat (#5002)
oxoxDev Jul 16, 2026
3038d73
test: point tests at web_chat (#5002)
oxoxDev Jul 16, 2026
fc78cfe
docs: refresh conduit path references to web_chat (#5002)
oxoxDev Jul 16, 2026
bd78e9c
docs: refresh stale conduit path in agent_harness_e2e comment (#5002)
oxoxDev Jul 16, 2026
b14ed89
refactor(web_chat): name the surface subscribers after their new modu…
oxoxDev Jul 16, 2026
50dad6b
refactor(web_chat): point the prompt-enforcement source label at web_…
oxoxDev Jul 17, 2026
bfd1997
docs(chat): refresh stale conduit path in artifact-events comment (#5…
oxoxDev Jul 17, 2026
b053d46
test(core): gate the two voice asserts for the disabled build (#5022)
oxoxDev Jul 17, 2026
b657f74
ci(feature-gate-smoke): run gate-contract tests + self-maintaining co…
oxoxDev Jul 17, 2026
a2fb63f
feat(learning): add always-on learning subscriber wiring module (#5003)
oxoxDev Jul 17, 2026
63c14a4
feat(core): register learning subscribers on always-on Platform boot …
oxoxDev Jul 17, 2026
0ad236a
refactor(channels): stop wiring platform learning subscribers in star…
oxoxDev Jul 17, 2026
fc3e3f5
test(learning): cover channel-independent subscriber wiring + idempot…
oxoxDev Jul 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 66 additions & 1 deletion .github/workflows/ci-lite.yml
Original file line number Diff line number Diff line change
Expand Up @@ -380,11 +380,16 @@ jobs:
needs: [changes]
if: needs.changes.outputs['rust-core'] == 'true' || needs.changes.outputs['rust-tauri'] == 'true'
runs-on: ubuntu-22.04
timeout-minutes: 25
# Was 25; raised because the lane now links (--all-targets) and runs a test
# binary, not just `cargo check`.
timeout-minutes: 40
container:
image: ghcr.io/tinyhumansai/openhuman_ci:rust-1.93.0
env:
CARGO_INCREMENTAL: "0"
# Deep-async-stack belt (same as the coverage lanes) β€” virtual reservation,
# zero cost. Keeps a gate-contract test with a deep stack from tripping.
RUST_MIN_STACK: "67108864"
steps:
- name: Checkout code
uses: actions/checkout@v7
Expand All @@ -402,8 +407,68 @@ jobs:
shared-key: pr-rust-feature-gate-smoke

- name: Check core builds with the default domain gates disabled
# Lib-scoped on purpose. `--all-targets` would additionally compile the
# `tests/` integration crates, which today name gated symbols
# (voice::reply_speech::test_seam, mcp_registry::store, x402::tools) without
# a #[cfg] and so do not compile with the gates off. That cleanup is tracked
# in #5021 alongside running the full gates-off suite; until it lands, this
# lane checks the lib and RUNS the gate-contract lib tests below.
run: bash scripts/ci-cancel-aware.sh cargo check --manifest-path Cargo.toml --no-default-features --features tokenjuice-treesitter

- name: Run the gate-contract tests with the default domain gates disabled
# `cargo check` (even --all-targets) never RUNS tests, so a gated family that
# is asserted WITHOUT a matching #[cfg] compiles fine and only fails at
# runtime β€” exactly how the two ungated `voice` asserts sat red on main
# (#5022). This step executes the gate-contract test modules so that class of
# regression fails CI, not just a local pre-push.
#
# Scope is deliberate: the full gates-off `--lib` run aborts on a pre-existing
# task_local stack overflow (`agent::harness::session::tests::
# turn_dispatches_spawn_subagent_through_full_path`) that reproduces in the
# gates-ON build too, and also OOMs unscoped. These filters route AROUND that
# test by scope, not by skip. Running the full suite is tracked in #5021.
run: |
bash scripts/ci-cancel-aware.sh cargo test --manifest-path Cargo.toml \
--no-default-features --features tokenjuice-treesitter --lib -- \
core::all:: core::cli:: core::jsonrpc:: core::legacy_aliases:: agent_registry::agents::loader::

- name: Guard β€” new feature-gated test modules must be acknowledged
# Self-maintaining coverage: the set of source files that #[cfg]-gate a test on
# a domain feature must equal the allowlist below. When a new gate (or a new
# test in an existing gate's domain) adds a gated test file, this fails β€” the
# author then adds it here AND, if it can hold an ungated-assert regression of
# the #5022 class, extends the scoped `cargo test` filter above. Prevents the
# smoke lane from silently under-covering as the gate surface grows.
run: |
set -euo pipefail
EXPECTED=$(cat <<'EOF'
core/all_tests.rs
core/cli_tests.rs
core/jsonrpc_tests.rs
core/legacy_aliases.rs
openhuman/agent/harness/builtin_definitions.rs
openhuman/agent/harness/definition_tests.rs
openhuman/agent/harness/session/tests.rs
openhuman/agent/harness/subagent_runner/tool_prep.rs
openhuman/agent_registry/agents/loader.rs
openhuman/inference/local/mod.rs
openhuman/tool_registry/ops_tests.rs
openhuman/tool_registry/schemas.rs
openhuman/tools/ops_tests.rs
openhuman/voice/compile_status.rs
openhuman/wallet/stub.rs
openhuman/web3/stub.rs
openhuman/x402/stub.rs
EOF
)
ACTUAL=$(grep -rlE '#\[cfg\((not\()?feature = "(voice|media|web3|meet|mcp|skills|flows)"' src --include='*.rs' \
| xargs grep -lE '#\[test\]|#\[tokio::test\]|fn .*_test' 2>/dev/null | sed 's|^src/||' | sort -u)
if ! diff <(echo "$EXPECTED" | sed 's/^ *//' | sort -u) <(echo "$ACTUAL"); then
echo "::error::Gated-test file set changed. Update the EXPECTED allowlist in the rust-feature-gate-smoke lane, and extend the scoped 'cargo test' filter if the new module can carry an ungated-assert regression (see #5022)."
exit 1
fi
echo "gate-contract test coverage allowlist is current"

rust-core-coverage:
name: Rust Core Coverage (cargo-llvm-cov)
needs: [changes, rust-quality]
Expand Down
2 changes: 1 addition & 1 deletion app/src-tauri/src/claude_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ end tell"#;
.args(["-e", script])
.spawn()
.map_err(|e| format!("failed to open Terminal.app: {e}"))?;
return Ok("Terminal.app".into());
Ok("Terminal.app".into())
}

#[cfg(target_os = "linux")]
Expand Down
2 changes: 1 addition & 1 deletion app/src-tauri/src/imessage_scanner/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -372,7 +372,7 @@ fn extract_text_from_attributed_body(blob: &[u8]) -> Option<String> {
.filter_map(|r| String::from_utf8(r).ok())
.filter(|s| {
let trimmed = s.trim();
trimmed.len() >= 2 && !ignored_markers.iter().any(|m| trimmed == *m)
trimmed.len() >= 2 && !ignored_markers.contains(&trimmed)
})
.max_by_key(|s| s.len())
.map(|s| s.trim().to_string())
Expand Down
8 changes: 4 additions & 4 deletions app/src-tauri/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1195,7 +1195,7 @@ fn mascot_window_show(app: AppHandle<AppRuntime>) -> Result<(), String> {
log::info!("[mascot-window] show requested");
#[cfg(target_os = "macos")]
{
return mascot_native_window::show(&app);
mascot_native_window::show(&app)
}
#[cfg(not(target_os = "macos"))]
{
Expand Down Expand Up @@ -1258,11 +1258,11 @@ fn notch_window_show(app: AppHandle<AppRuntime>) -> Result<(), String> {
log::info!("[notch-window] show requested");
#[cfg(target_os = "macos")]
{
return dispatch_notch_on_main(app, |app| {
dispatch_notch_on_main(app, |app| {
if let Err(e) = notch_window::show(app) {
log::warn!("[notch-window] show failed: {e}");
}
});
})
}
#[cfg(not(target_os = "macos"))]
{
Expand All @@ -1277,7 +1277,7 @@ fn notch_window_hide(app: AppHandle<AppRuntime>) -> Result<(), String> {
log::info!("[notch-window] hide requested");
#[cfg(target_os = "macos")]
{
return dispatch_notch_on_main(app, |_app| notch_window::hide());
dispatch_notch_on_main(app, |_app| notch_window::hide())
}
#[cfg(not(target_os = "macos"))]
{
Expand Down
5 changes: 4 additions & 1 deletion app/src-tauri/src/mascot_native_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ const DRAG_POLL_SECONDS: f64 = 0.016;
/// dropped webview.
struct MascotPanel {
panel: Retained<NSPanel>,
// RAII keep-alive: never read, but dropping it deallocates the WKWebView and
// blanks the panel. Must outlive the show/hide cycle β€” do not remove.
#[allow(dead_code)]
webview: Retained<WKWebView>,
drag_timer: Retained<NSTimer>,
}
Expand Down Expand Up @@ -389,7 +392,7 @@ unsafe fn build_webview(
let _: () = msg_send![&*webview, setAutoresizingMask: 18u64]; // width|height

// Make the webview the panel's content view so it fills the frame.
let webview_ref: &objc2::runtime::AnyObject = &*webview;
let webview_ref: &objc2::runtime::AnyObject = &webview;
let webview_view: *mut objc2::runtime::AnyObject =
webview_ref as *const _ as *mut objc2::runtime::AnyObject;
let _: () = msg_send![panel, setContentView: webview_view];
Expand Down
4 changes: 2 additions & 2 deletions app/src-tauri/src/native_notifications/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ use crate::AppRuntime;
pub fn notification_permission_state() -> Result<String, String> {
#[cfg(target_os = "macos")]
{
return macos::permission_state();
macos::permission_state()
}
#[cfg(not(target_os = "macos"))]
{
Expand All @@ -51,7 +51,7 @@ pub fn notification_permission_state() -> Result<String, String> {
pub fn notification_permission_request() -> Result<String, String> {
#[cfg(target_os = "macos")]
{
return macos::request_permission();
macos::request_permission()
}
#[cfg(not(target_os = "macos"))]
{
Expand Down
6 changes: 1 addition & 5 deletions app/src-tauri/src/notch_window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,6 @@ thread_local! {
static NOTCH: RefCell<Option<NotchPanel>> = const { RefCell::new(None) };
}

pub(crate) fn is_open() -> bool {
NOTCH.with(|cell| cell.borrow().is_some())
}

pub(crate) fn hide() {
NOTCH.with(|cell| {
if let Some(existing) = cell.borrow_mut().take() {
Expand Down Expand Up @@ -262,7 +258,7 @@ unsafe fn build_webview(
// Auto-resize to fill the panel content view.
let _: () = msg_send![&*webview, setAutoresizingMask: 18u64]; // width|height

let webview_ref: &objc2::runtime::AnyObject = &*webview;
let webview_ref: &objc2::runtime::AnyObject = &webview;
let webview_view = webview_ref as *const _ as *mut objc2::runtime::AnyObject;
let _: () = msg_send![panel, setContentView: webview_view];

Expand Down
2 changes: 1 addition & 1 deletion app/src/services/chatService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -966,7 +966,7 @@ export function subscribeChatEvents(listeners: ChatEventListeners): () => void {
}

// Artifact lifecycle events (#2779). The Rust subscriber in
// `channels/providers/web::ArtifactSurfaceSubscriber` packs the
// `web_chat::ArtifactSurfaceSubscriber` packs the
// artifact payload into the generic `args` field of the wire
// envelope (kept the WebChannelEvent struct shape stable to avoid
// touching ~10 existing call sites with `..Default::default()`).
Expand Down
2 changes: 1 addition & 1 deletion docs/TEST-COVERAGE-MATRIX.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ Canonical mapping of every product feature to its test source(s). Drives gap-fil
| 4.2.1 | User Message Handling | WD+RI | `conversations-web-channel-flow.spec.ts`, `tests/json_rpc_e2e.rs` | βœ… | |
| 4.2.2 | AI Response Generation | WD | `agent-review.spec.ts` | βœ… | Mock LLM |
| 4.2.3 | Streaming Responses | RI | `tests/json_rpc_e2e.rs`, `tests/agent_harness_e2e.rs` | βœ… | `tests/agent_harness_e2e.rs` adds provider-level SSE tool-arg accumulation (chunked args reassembled + parsed) and engine-level delta forwarding (#3471) |
| 4.2.4 | Parallel inference (cross-thread + within-thread forked turns) | RU+VU | `src/openhuman/channels/providers/web_tests.rs`, `app/src/store/__tests__/chatRuntimeSlice.test.ts`, `app/src/providers/__tests__/ChatRuntimeProvider.test.tsx` | 🟑 | Concurrent same-/cross-thread dispatch, cooperative `CancellationToken` teardown, and parallel-lane stream routing covered; dedicated WD E2E is a follow-up |
| 4.2.4 | Parallel inference (cross-thread + within-thread forked turns) | RU+VU | `src/openhuman/web_chat/web_tests.rs`, `app/src/store/__tests__/chatRuntimeSlice.test.ts`, `app/src/providers/__tests__/ChatRuntimeProvider.test.tsx` | 🟑 | Concurrent same-/cross-thread dispatch, cooperative `CancellationToken` teardown, and parallel-lane stream routing covered; dedicated WD E2E is a follow-up |
| 4.2.5 | Per-thread todo list (plan strip above composer) | RU+VU+WD | `src/openhuman/agent/tools/todo.rs`, `app/src/pages/conversations/components/ThreadTodoStrip.test.tsx`, `app/test/e2e/specs/chat-thread-todo-strip.spec.ts` | βœ… | Read-only thread-scoped todo strip fed by `task_board_updated`; agent `todo` tool guidance + thread binding; E2E drives a `todo` tool call and asserts the card renders |
| 4.2.6 | Background-activity panel (chat-header Background tasks button) | VU+WD | `app/src/pages/conversations/hooks/useBackgroundActivity.test.ts`, `app/src/pages/conversations/components/__tests__/BackgroundActivityRows.test.tsx`, `app/test/e2e/specs/chat-background-activity-panel.spec.ts` | βœ… | View-only panel surfacing this chat's async sub-agents + global cron jobs, subconscious/heartbeat status, and memory syncing; freshness-only "Syncing now" labeling; E2E opens the panel and asserts its sections render and close |
| 4.2.7 | Plan-mode review (Approve / Reject / Send-feedback before execute) | RU+RI+VU | `src/openhuman/plan_review/gate.rs`, `src/openhuman/plan_review/tool.rs`, `src/openhuman/plan_review/schemas.rs`, `tests/json_rpc_e2e.rs`, `app/src/pages/conversations/components/PlanReviewCard.test.tsx`, `app/src/pages/__tests__/Conversations.render.test.tsx` | βœ… | Interactive turns call `request_plan_review`, which parks the LIVE turn on the in-memory `PlanReviewGate` (oneshot) until the user decides; `plan_review_request` socket event drives `PlanReviewCard`, which resolves via `openhuman.plan_review_decide` (approve resumes-and-executes / reject resumes-and-stops / revise resumes-with-feedback). RU covers gate park/resolve/timeout + tool auto-approve + parking; RI covers the decide RPC; VU covers the card + provider wiring. WD E2E (agent-driven park flow) tracked as follow-up |
Expand Down
2 changes: 1 addition & 1 deletion src/core/all.rs
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,7 @@ fn build_registered_controllers() -> Vec<GroupedController> {
push(
&mut controllers,
DomainGroup::Channels,
crate::openhuman::channels::providers::web::all_web_channel_registered_controllers(),
crate::openhuman::web_chat::all_web_channel_registered_controllers(),
);
push(
&mut controllers,
Expand Down
6 changes: 6 additions & 0 deletions src/core/all_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,9 @@ async fn harness_excludes_gated_namespaces() {
.collect();
#[cfg(feature = "flows")]
assert!(full_ns.contains("flows"), "full() must expose flows");
// `voice` was the pathfinder gate (#4803) and predates this per-assert cfg
// convention; gate it like its siblings so the disabled build passes (#5022).
#[cfg(feature = "voice")]
assert!(full_ns.contains("voice"), "full() must expose voice");

let ctx = CoreContext::for_test(DomainSet::harness(), None);
Expand Down Expand Up @@ -958,6 +961,9 @@ fn group_mapping_smoke() {
// has no controller to map. Skip when its Cargo feature is off.
#[cfg(feature = "skills")]
assert_eq!(group_for_namespace("skills"), Some(DomainGroup::Skills));
// `voice` predates the per-assert cfg convention (#4803); registry-derived, so
// it has no entry to map when the feature is off. Gate like its siblings (#5022).
#[cfg(feature = "voice")]
assert_eq!(group_for_namespace("voice"), Some(DomainGroup::Voice));
#[cfg(feature = "web3")]
assert_eq!(group_for_namespace("wallet"), Some(DomainGroup::Web3));
Expand Down
4 changes: 2 additions & 2 deletions src/core/event_bus/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,7 @@ pub enum DomainEvent {
/// flow-approval-surface, PR2/PR3). Unlike `ApprovalRequested`, this
/// event carries no `thread_id`/`client_id` β€” a flow run has neither, so
/// the generic chat-routed socket bridge
/// (`channels::providers::web::event_bus::ApprovalSurfaceSubscriber`)
/// (`web_chat::event_bus::ApprovalSurfaceSubscriber`)
/// silently drops it (that gap was the original silent-deadlock bug).
/// Published by `ApprovalGate::intercept_audited` alongside the existing
/// `ApprovalRequested`, bridged by `core::socketio` directly to a
Expand Down Expand Up @@ -611,7 +611,7 @@ pub enum DomainEvent {
///
/// Bridged to the `external_transfer_pending` web-channel socket event by
/// `EgressSurfaceSubscriber` (defined in
/// `src/openhuman/channels/providers/web/event_bus.rs`) when the emitting
/// `src/openhuman/web_chat/event_bus.rs`) when the emitting
/// turn carries chat routing. `thread_id` / `client_id` come from the
/// ambient `APPROVAL_CHAT_CONTEXT` and are `None` for CLI / cron /
/// background transfers (no chat surface to route to).
Expand Down
Loading
Loading