Skip to content

fix(harness-init): bound the status poll loop instead of retrying forever (#5157) - #5276

Merged
M3gA-Mind merged 2 commits into
tinyhumansai:mainfrom
M3gA-Mind:fix/5157-harness-init-status
Jul 31, 2026
Merged

fix(harness-init): bound the status poll loop instead of retrying forever (#5157)#5276
M3gA-Mind merged 2 commits into
tinyhumansai:mainfrom
M3gA-Mind:fix/5157-harness-init-status

Conversation

@M3gA-Mind

@M3gA-Mind M3gA-Mind commented Jul 30, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Bound the HarnessInitOverlay status poll loop, which retried a failing RPC every 2s forever with no cap and no backoff — the source of 64,715 Sentry events (~9k/day) from a single client (CORE-RUST-1PY).
  • Added a method_not_found CoreRpcError kind so pollers can recognise a permanent RPC miss and stop, instead of treating it as transient.
  • Capped non-permanent failures at 5 attempts with 2s→30s exponential backoff, while preserving the cold-start retry the loop exists for.
  • Corrected the KNOWN_PROBE_METHODS rationale in src/core/dispatch.rs, which described harness_init_status as a retired method — it is live and served.
  • Pinned the controller registration with a test, so a genuine regression fails loudly rather than being silenced by that allow-list.

Problem

HarnessInitOverlay polls openhuman.harness_init_status every 2s. On any error it called setTimeout(poll, POLL_MS) unconditionally — no attempt cap, no backoff, and no concept of a failure that can never succeed. Against a core that does not serve the method this became a permanent 30-calls-per-minute loop for the life of the window, and because the core records every unknown-method miss it produced 64,715 events.

The issue's framing is inverted, and it changes the fix. harness_init_status is not a removed RPC. It is a live, registered controller (harness_init::all_harness_init_registered_controllers, tagged DomainGroup::Platform, covered by json_rpc_harness_init_status_returns_snapshot_envelope). It misses only when caller and core disagree about the served surface:

Those are legitimate configurations, so the first miss is expected and correct. The amplification from 1 to 64,715 is entirely client-side.

This also means the existing server-side allow-list entry (added in #5171) cannot have fixed the reported events: the affected cores are 0.57.5, versions that shipped long before that allow-list existed.

Solution

Client — stop the amplification at its source.

  • classifyRpcError maps an unknown method: response to a new method_not_found kind, prefix-anchored to mirror dispatch::unknown_method_name's strip_prefix, so the two classifiers cannot drift. Exposed as isMethodNotFoundCoreRpcError so callers branch on kind rather than a message regex — which is what that module's own doc-comment already requires.
  • The overlay stops polling immediately on method_not_found and renders nothing: a core without harness_init has no init run to report.
  • Any other failure gets at most MAX_TRANSIENT_FAILURES (5) attempts with exponential backoff capped at 30s, so even an unforeseen persistent fault decays and stops rather than running forever.
  • The failure budget resets on the first success, keeping the legitimate "core is still booting" retry intact.

Core — correct a misleading note and close the observability hole it opened.

The KNOWN_PROBE_METHODS comment listed harness_init_status alongside genuinely retired calls, as a "retired feature call ... when no safe canonical handler exists". It is served, so that note invited deleting a live controller. And because the allow-list makes the miss debug-only, a genuine regression — the controller dropped from the registry — would have gone completely silent: no error, no warn, no Sentry event. The comment now states the real reason (surface skew), and harness_init_status_is_registered_in_a_full_build pins that the method really is served.

Tradeoff considered: removing the allow-list entry instead. Rejected — the miss is legitimate under domain-gated and slim builds, so it should stay debug-only; the correct guard is a test that fails on regression, not a Sentry event on a supported configuration.

Submission Checklist

  • Tests added or updated (happy path + at least one failure / edge case) per Testing Strategy
  • Diff coverage ≥ 80% — every changed branch is exercised: permanent-miss stop, bounded transient retry, success-resets-budget, classifier prefix anchoring (positive + negative), and the Rust registration pin.
  • Coverage matrix updated — N/A: behaviour-only change; feature row 0.2.5 already covers harness_init_status and no feature was added, removed, or renamed.
  • All affected feature IDs from the matrix are listed in the PR description under ## Related
  • No new external network dependencies introduced (mock backend used per Testing Strategy)
  • Manual smoke checklist updated if this touches release-cut surfaces — N/A: no release-cut surface changed; the overlay's visible states are unchanged.
  • Linked issue closed via Closes #NNN in the ## Related section

Impact

  • Platform: desktop (Tauri/React renderer) plus a comment/test-only change in the Rust core. No behaviour change to the core's RPC surface or wire contract.
  • User-visible: none on a healthy install. On a core that does not serve harness_init_status the overlay now stays hidden instead of silently retrying forever — the same rendered result, without the traffic.
  • Observability: removes ~9k Sentry events/day per affected client. The remaining first-miss is still recorded core-side at debug.
  • Performance: removes a permanent 30 RPC/minute background loop in the skew case.
  • Compatibility: additive only. method_not_found is a new CoreRpcErrorKind variant; no existing classification changes, and the JSON-RPC response to callers is untouched.

Related


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

Linear Issue

Commit & Branch

  • Branch: fix/5157-harness-init-status
  • Commit SHA: e9700acaced960304c1a238d9381d6957d5a5b37

Validation Run

  • pnpm --filter openhuman-app format:check — Prettier applied to all four changed frontend files; ESLint clean.
  • pnpm typechecktsc --noEmit clean.
  • Focused tests: vitest run HarnessInitOverlay.test.tsx coreRpcClient.test.ts108 passed.
  • Rust fmt/check (if changed): rustfmt --check src/core/dispatch.rs clean. Full cargo check / cargo test was not run locally — that verification is delegated to CI and is itemised under Validation Blocked below.
  • Tauri fmt/check (if changed): N/A — no change under app/src-tauri/.

Validation Blocked

  • command: cargo test --lib core::dispatch
  • error: not run — full core builds were intentionally skipped locally on this machine.
  • impact: the new Rust test harness_init_status_is_registered_in_a_full_build is unverified by compilation. Its two APIs (core::all::all_controller_schemas, core::all::rpc_method_name) were confirmed present and type-compatible by inspection, and DomainSet scoping is tokio::task_local! so the test cannot be contaminated by the domain-gating tests in the same binary. CI is the gate.

Behavior Changes

  • Intended behavior change: a status poll that fails permanently now stops instead of retrying every 2s indefinitely; other failures are capped and backed off.
  • User-visible effect: none in the healthy case. In the skew case the overlay renders nothing (as before) but no longer generates continuous background RPC traffic.

Parity Contract

  • Legacy behavior preserved: the cold-start retry path is unchanged for the first 4 consecutive failures, and the consecutive-failure budget resets on any success. The dismissal/remount semantics from Show the runtime setup popup only on first launch #5047 and the StrictMode poll coalescing are untouched — their existing tests still pass.
  • Guard/fallback/dispatch parity checks: the frontend UNKNOWN_METHOD_PREFIX mirrors the Rust constant in src/core/dispatch.rs, and the match is prefix-anchored exactly as unknown_method_name uses strip_prefix. A test pins that a nested/quoted occurrence does not classify as method_not_found. The JSON-RPC error envelope returned to callers is unchanged.

Duplicate / Superseded PR Handling

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

Regression evidence

The new overlay tests fail against the pre-fix code and pass after — reverting only HarnessInitOverlay.tsx and re-running:

× stops polling when the core does not expose harness_init_status (#5157)
    AssertionError: expected "vi.fn()" to be called 1 times, but got 61 times
× gives up after a bounded number of consecutive transient failures (#5157)
    AssertionError: expected "vi.fn()" to be called 5 times, but got 61 times

61 calls in the same window is the 64,715-event generator, reproduced.

…ever (tinyhumansai#5157)

`HarnessInitOverlay` polled `openhuman.harness_init_status` every 2s and, on
*any* failure, rescheduled unconditionally for the life of the window — no cap,
no backoff. Against a core that does not serve the method this became a
permanent 30-calls-per-minute loop, and since the core records every miss it
produced 64,715 Sentry events (~9k/day) from a single client (CORE-RUST-1PY).

The method is not retired: it is a live controller tagged `DomainGroup::Platform`.
It legitimately misses on client/core surface skew — an older core behind a newer
UI bundle, a runtime `DomainSet` without `Platform` (e.g. `DomainSet::harness()`),
or a slim feature build. That first miss is expected; the 64,714 that follow are
the client refusing to accept a permanent answer.

- classify `unknown method: ` responses as a new `method_not_found` RPC error
  kind, prefix-anchored to mirror `dispatch::unknown_method_name`'s `strip_prefix`,
  and expose `isMethodNotFoundCoreRpcError` so pollers branch on `kind` rather
  than a message regex
- stop the overlay poll on `method_not_found` (permanent — retrying an absent
  method can never succeed), and cap other failures at 5 attempts with 2s→30s
  exponential backoff so no fault can poll forever
- keep the cold-start retry the loop exists for: the failure budget resets on
  the first success

Also corrects the `KNOWN_PROBE_METHODS` rationale, which listed the method as a
retired call with no canonical handler. It is served, so that note invited
deleting a live controller — and because the allow-list makes the miss
debug-only, a genuine regression would have gone silent in Sentry. Pins the
registration with a test so the regression fails loudly instead.

Regression coverage: without the overlay fix the new tests observe 61 calls in
the same window where the fix makes 1 (absent method) and 5 (persistent fault).
@M3gA-Mind
M3gA-Mind requested a review from a team July 30, 2026 13:02

@greptile-apps greptile-apps 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.

M3gA-Mind has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 13 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e73afd60-815c-4446-9fe0-58856854b6e5

📥 Commits

Reviewing files that changed from the base of the PR and between bb83836 and 2ac7ad7.

📒 Files selected for processing (5)
  • app/src/components/InitProgressScreen/HarnessInitOverlay.test.tsx
  • app/src/components/InitProgressScreen/HarnessInitOverlay.tsx
  • app/src/services/__tests__/coreRpcClient.test.ts
  • app/src/services/coreRpcClient.ts
  • src/core/dispatch.rs

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

@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: e9700acace

ℹ️ 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 app/src/components/InitProgressScreen/HarnessInitOverlay.tsx Outdated
…budget

The retry cap added for tinyhumansai#5157 could strand the blocking overlay. `shouldShow`
includes `running`, so once a run is in progress the overlay covers the app; if
the core then had a transient outage lasting more than MAX_TRANSIENT_FAILURES
attempts, the loop gave up and left that `running` snapshot on screen with stale
progress for the rest of the session — even after the core recovered and the run
reached `done`. The pre-tinyhumansai#5157 loop recovered from exactly that, so this was a
regression the bound introduced.

Split the give-up decision by whether anything blocking is displayed:

- nothing on screen (the tinyhumansai#5157 case — a core that never serves the method, no
  UI, a silent 30-calls-per-minute loop): give up at the cap, unchanged;
- a `running` overlay on screen: keep watching, but drop to STALLED_POLL_MS
  (30s). That is 2 calls/min — 15x below the runaway loop tinyhumansai#5157 fixed — and it
  only runs while a blocking overlay is actually up.

`awaitingTerminalRef` mirrors that condition so the failure branch can read it
without re-running the effect, and `isBlockingSnapshot` is now shared between
the poll loop and the render path so the two cannot drift.

Regression test drives a live run, then an outage well past the budget, then
recovery, and asserts the overlay observes the terminal snapshot and clears
itself. Verified failing before the fix: polling stopped at 6 calls and the
overlay stayed on screen.

Addresses the Codex review on tinyhumansai#5276.

@greptile-apps greptile-apps 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.

M3gA-Mind has reached the 50-credit limit for trial accounts. To continue receiving code reviews, upgrade your plan.

@M3gA-Mind
M3gA-Mind merged commit eb2fa8c into tinyhumansai:main Jul 31, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

unknown method: openhuman.harness_init_status — old client calling removed RPC

1 participant