Skip to content

feat(observatory): surface server rejections from the steer controls#1421

Merged
jaylfc merged 2 commits into
devfrom
feat/observatory-steer-errors
Jun 23, 2026
Merged

feat(observatory): surface server rejections from the steer controls#1421
jaylfc merged 2 commits into
devfrom
feat/observatory-steer-errors

Conversation

@jaylfc

@jaylfc jaylfc commented Jun 23, 2026

Copy link
Copy Markdown
Owner

What

Follow-up to the kilo review on #1418 / #1420. The pause, global-cap, and per-lane-cap controls all posted optimistically without inspecting res.ok, so a server rejection (4xx/5xx: forbidden, unknown lane, rejected value) left the optimistic value standing with no user-visible signal until the 5s poll reconciled.

Change

  • Route all three steer writes through a shared postSteer(url, body, failMsg) that checks res.ok, sets a dismissible error banner on failure, clears it on the next success, and always reconciles via the silent reload.
  • Done uniformly across setScope / setGlobalCap / setLaneCap so the controls stay consistent (rather than patching only the lane path), which is why this is a dedicated follow-up rather than a fold on feat(observatory): per-lane concurrency caps (steer v1 complete) #1420.

Tests

  • ObservatoryApp.test.tsx: 2 new cases (error shown when a steer post is rejected; cleared after a later success) plus the existing 11. All 13 green; tsc -b clean.

Verification note

Behaviourally verified via vitest; visual check of the banner deferred to a live session, as with the other desktop slices.

Summary by CodeRabbit

Release Notes

  • Bug Fixes

    • Steer control operations now show dismissible red error alert banners when pause queue or cap update requests fail.
    • Error banners automatically clear after the next successful retry, even if multiple requests occur in quick succession.
  • Tests

    • Added coverage for pause queue error handling, including showing the error after a failed attempt and clearing it after a subsequent success.

Follow-up to the kilo review on #1418/#1420: pause, global cap, and per-lane cap
all posted optimistically without inspecting res.ok, so a 4xx/5xx (forbidden,
unknown lane, rejected value) left the optimistic value standing with no signal.
Route all three through a shared postSteer that checks res.ok, shows a
dismissible error banner on failure, clears it on the next success, and always
reconciles against the server. Done uniformly so the three controls stay
consistent rather than patching one. Two new tests (error shown on a rejected
post; cleared after a later success); the existing 11 pass through the refactor.
@qodo-code-review

Copy link
Copy Markdown

Qodo reviews are paused for this user.

Troubleshooting steps vary by plan Learn more →

On a Teams plan?
Reviews resume once this user has a paid seat and their Git account is linked in Qodo.
Link Git account →

Using GitHub Enterprise Server, GitLab Self-Managed, or Bitbucket Data Center?
These require an Enterprise plan - Contact us
Contact us →

@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: fea83e1e-66da-4470-af04-b4769c1314db

📥 Commits

Reviewing files that changed from the base of the PR and between 5d95286 and 1ac40f9.

📒 Files selected for processing (1)
  • desktop/src/apps/ObservatoryApp.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
  • desktop/src/apps/ObservatoryApp.tsx

📝 Walkthrough

Walkthrough

ObservatoryApp gains a steerError state and a postSteer useCallback that centralizes POST logic for pause, throttle, and per-lane endpoints, setting error state on failure and reconciling UI via load({ silent: true }) in a finally block. A dismissible alert banner renders when steerError is set. Two tests validate error display and clearance.

Changes

Steer POST Error Handling

Layer / File(s) Summary
steerError state and postSteer helper
desktop/src/apps/ObservatoryApp.tsx
Adds steerError React state, AlertCircle import, useRef for request sequencing, and the shared postSteer useCallback that POSTs JSON, sets steerError on non-OK responses or exceptions, and always calls load({ silent: true }) in finally.
Steer control delegation
desktop/src/apps/ObservatoryApp.tsx
Removes inline try/catch/finally POST blocks from setScope (pause), setGlobalCap (global throttle), and setLaneCap (per-lane throttle); all now call postSteer with the respective endpoint and error message.
Dismissible error banner UI
desktop/src/apps/ObservatoryApp.tsx
Adds conditional rendering of a red alert banner with AlertCircle and a Dismiss button that clears steerError when visible.
Error display and recovery tests
desktop/src/apps/ObservatoryApp.test.tsx
Two new tests cover the pause endpoint: one asserts the error banner appears on a 403 failure, the other asserts the banner appears after failure and is removed after the next successful POST.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Poem

🐇 A steer went wrong, the queue cried "Nay!"
Now errors surface without delay.
A banner glows red, clear as can be,
One dismiss click sets the UI free.
The rabbit refactored, tests in tow —
No silent failures steal the show! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the primary change: adding server rejection handling (surfacing/displaying errors) for steer controls in the Observatory app.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/observatory-steer-errors

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Comment thread desktop/src/apps/ObservatoryApp.tsx Outdated
headers: { "Content-Type": "application/json" },
body: JSON.stringify(body),
});
setSteerError(res.ok ? null : failMsg);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

WARNING: The server's rejection body is discarded, so the user sees only the generic failMsg even when the API returned a specific reason.

The PR description explicitly motivates this change on the cases "forbidden", "unknown lane", and "rejected value" — the whole point is that the server can tell the user why the write was refused, and currently that detail is thrown away by postSteer (no res.json() call, no body.detail extraction). The new test on line 239 even mocks body: { detail: "forbidden" }, which would be a useful string to surface — but the production code never reads it.

Consider parsing the response body on !res.ok and preferring body.detail (or body.message) over the hard-coded failMsg, with the generic message kept as a fallback when the body is missing or unparseable. This is the highest-value gap in this change.

Comment thread desktop/src/apps/ObservatoryApp.tsx Outdated
});
setSteerError(res.ok ? null : failMsg);
} catch {
setSteerError(failMsg);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

SUGGESTION: The catch branch reuses the same caller-supplied failMsg ("Could not update the pause state.") that the !res.ok branch uses, which is misleading for network/CORS/abort failures.

For a fetch that throws before any response is received, the user is told the server rejected the state when in reality the request never arrived. Consider a distinct message for the thrown branch (e.g. "Could not reach the server.") or, better, inline whatever detail err carries. Low priority but cheap to fix while you're already touching this path.

@kilo-code-bot

kilo-code-bot Bot commented Jun 23, 2026

Copy link
Copy Markdown

Code Review Summary

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
desktop/src/apps/ObservatoryApp.tsx 148 postSteer discards the server's rejection body, so the user only sees the generic failMsg even though the PR's stated motivation is surfacing specific reasons ("forbidden", "unknown lane", "rejected value"). The new test on line 239 mocks body: { detail: "forbidden" } but the production code never reads it. Parse the response body on !res.ok and prefer body.detail over the hard-coded failMsg. (Re-verified on changed code; author deferred as polish.)

SUGGESTION

File Line Issue
desktop/src/apps/ObservatoryApp.tsx 150 The catch branch reuses the same caller-supplied failMsg as the !res.ok branch, which misleads users when the request never reached the server (network/CORS/abort). Consider a distinct message or surface err.message. (Re-verified on changed code; author deferred as polish.)
Files Reviewed (2 files)
  • desktop/src/apps/ObservatoryApp.tsx - 2 issues (both carried forward; the CodeRabbit race-condition finding from the previous review was resolved by the new steerSeq guard at lines 138–150)
  • desktop/src/apps/ObservatoryApp.test.tsx - 0 issues

Fix these issues in Kilo Cloud

Previous Review Summary (commit 5d95286)

Current summary above is authoritative. Previous snapshots are kept for context only.

Previous review (commit 5d95286)

Status: 2 Issues Found | Recommendation: Address before merge

Overview

Severity Count
CRITICAL 0
WARNING 1
SUGGESTION 1
Issue Details (click to expand)

WARNING

File Line Issue
desktop/src/apps/ObservatoryApp.tsx 143 postSteer discards the server's rejection body, so the user only sees the generic failMsg even though the PR's stated motivation is surfacing specific reasons ("forbidden", "unknown lane", "rejected value"). The new test on line 239 mocks body: { detail: "forbidden" } but the production code never reads it. Parse the response body on !res.ok and prefer body.detail over the hard-coded failMsg.

SUGGESTION

File Line Issue
desktop/src/apps/ObservatoryApp.tsx 145 The catch branch reuses the same caller-supplied failMsg as the !res.ok branch, which misleads users when the request never reached the server (network/CORS/abort). Consider a distinct message or surface err.message.
Files Reviewed (2 files)
  • desktop/src/apps/ObservatoryApp.tsx - 2 issues
  • desktop/src/apps/ObservatoryApp.test.tsx - 0 issues

Fix these issues in Kilo Cloud


Reviewed by minimax-m3 · Input: 44.4K · Output: 2.6K · Cached: 88.7K

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 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 `@desktop/src/apps/ObservatoryApp.tsx`:
- Around line 132-149: The postSteer function does not guard against race
conditions where concurrent requests complete out of order, potentially clearing
newer errors with older successes. Introduce a request sequence counter that
increments each time postSteer is called, capture the current sequence number at
the start of each postSteer invocation, and gate the setSteerError calls to only
execute if the current request sequence matches the latest sequence number. This
ensures only the most recent in-flight request can update the steerError state,
preventing stale error states from being overwritten by older request
completions.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 1f6d3210-0468-418b-bf4c-da21a0fdf3ee

📥 Commits

Reviewing files that changed from the base of the PR and between e4a82d6 and 5d95286.

📒 Files selected for processing (2)
  • desktop/src/apps/ObservatoryApp.test.tsx
  • desktop/src/apps/ObservatoryApp.tsx

Comment thread desktop/src/apps/ObservatoryApp.tsx
@gitar-bot

gitar-bot Bot commented Jun 23, 2026

Copy link
Copy Markdown

Note

Your trial team has used its Gitar budget, so automatic reviews are paused. Upgrade now to unlock full capacity. Comment "Gitar review" to trigger a review manually.
Learn more about usage limits

Code Review ✅ Approved

Consolidates steer control writes into a unified postSteer handler that validates server responses, ensuring optimistic updates are correctly reverted upon rejection. No issues found.

Options

Display: compact → Showing less information.

Comment with these commands to change:

Compact
gitar display:verbose         

Important

Your trial ends in 3 days — upgrade now to keep code review, CI analysis, auto-apply, custom automations, and more.

Was this helpful? React with 👍 / 👎 | Gitar

…rites

Fold the CodeRabbit Minor: steer controls are not fully serialized (pause and a
cap change use different busy scopes), so two postSteer calls can resolve out of
order and a stale result could overwrite a newer one's banner. Stamp each call
with a sequence number and only let the latest one set steerError. Deferred the
two kilo nits (surfacing the server's specific reason, and distinguishing a
network failure from a server rejection) as polish; the generic message is an
intentional, accurate choice.
@jaylfc jaylfc enabled auto-merge (squash) June 23, 2026 19:58
@jaylfc jaylfc merged commit 3397394 into dev Jun 23, 2026
8 checks passed
@github-project-automation github-project-automation Bot moved this from Todo to Done in TinyAgentOS Roadmap Jun 23, 2026
jaylfc added a commit that referenced this pull request Jun 23, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Development

Successfully merging this pull request may close these issues.

1 participant