fix(ops): stop the channels panel reporting success it cannot observe - #59
Merged
Merged
Conversation
Saving a Telegram allowlist showed a green toast and then broke. Three defects turned a backend restart into a silent failure, and a fourth silently revoked people. - `notify` used `else if`, so the gateway's "the runtime is reloading" note was suppressed whenever a `warning` was also set — and the gateway sets `warning` when the list is empty or contains `*`, which are exactly the two states an operator is most likely to be in while editing. Both surface now. - The toast asserted "Allowlist updated" before anything was observable. It reports the count the SERVER stored, so a mismatch with what was typed is visible. - `PanelFrame` swapped children for the error state on any error, so a refresh failure blanked the panel — making the most likely outcome of a *successful* save an error screen, indistinguishable from the save having failed. It now distinguishes an initial-load failure (error state is right) from a refresh failure (keep the content, show a non-blocking strip), using a `loaded` flag `use-async` already tracked internally and did not expose. - The POST replaces the allowlist wholesale from a snapshot taken when the panel loaded, so anyone who self-onboarded via `/claim` since then was silently revoked — the backend re-reads the freshest config under a lock specifically to avoid that, and the console defeated it. `allowlistDrift` re-fetches before saving and, when the server has moved, names who would be removed and asks. Also: `use-async` gains the request-id guard from `use-async-guarded`, so a slow pre-restart response can no longer land last and show pre-save data; the panel's 3-second settle timer lives in a ref and is cleared on unmount instead of firing into an unmounted tree; and the panel consumes `use-gateway-status` to render a bounded "reloading the runtime" state that keeps its content and recovers on its own, naming the manual recovery if the gateway does not return within a minute.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Executes
plans/137-console-channels-panel-truth.md(the plan file lives in theRantAIClaw repo; the work is all here).
Problem
Saving a Telegram allowlist showed a green "Allowlist updated" toast and then
broke. Three defects turn a backend restart into a silent failure, and a fourth
silently revokes people.
immediately — racing a daemon restart the save itself triggered 750 ms later.
else if, in exactly the two states an operator is most likely to be inwhile editing: the gateway sets
warningwhen the list is empty or contains*, and puts the restart notice innote.successful save was an error screen — indistinguishable, to the operator,
from the save having failed.
possibly-stale snapshot, silently revoking anyone who self-onboarded via
/claimsince the panel was opened. The backend goes out of its way to avoidclobbering that — it re-reads the freshest config under a lock, with a comment
saying so — and the console defeated it.
Change, step by step
notifysurfaces awarningand anote; neither implies the other.allowlistDrift(seeded, server, next)re-fetches before POSTing and, when theserver has moved, lists who would be removed and asks via the
ConfirmModalalready in this file. The wholesale-replacement API contract is preserved —
making the revocation visible is the actual defect, and building a delta API
is out of scope. A failed pre-check falls through to the save rather than
blocking it: it is a courtesy, not a gate.
r.allowed_users— what the server stored.PanelFrametakesloadedand distinguishes an initial-load failure (errorstate is right) from a refresh failure (keep the content, show a non-blocking
strip).
use-asyncalready trackedloadedinternally; it now returns it.Callers that do not pass it keep the old behaviour.
use-asyncgains the request-id guard fromuse-async-guarded, with therefreshing/loadedsemantics unchanged — a slow pre-restart response can nolonger land last and show pre-save data. The 3-second settle timer moves into
a ref and is cleared on unmount.
use-gateway-statusand renders a "Reloading theruntime…" state that keeps its content, recovers on its own when the gateway
answers, and gives up after 60 s with the manual recovery named.
Step 7 (drive it in a browser): NOT done
The plan asks for four browser observations against a running gateway with a
live Telegram bot. I did not have a gateway or a bot token, so none of the four
were made — I am not claiming them. What replaced them:
allowlistDriftis extracted as a pure function and unit-tested, including the/claim-after-open case that motivated step 2, and mutation-checked (making itignore server additions fails two of its four tests).
bunx tsc --noEmit(via./node_modules/.bin/tsc) → exit 0.next build→ succeeded; the route table rendered, so this is a real build.vitest run→ 163 passed, 21 files (was 159 / 20).The race in step 5 and the reconnect in step 6 are the two that genuinely need a
browser. They should be driven before this ships.
Note on tooling
bun/bunxare not installed on this machine; the repo's binaries undernode_modules/.binwere used directly. There is no eslint config here, sobun run lintcontributes nothing — as the plan says.Scope
Only the four files the plan lists:
channels-panel.tsx,use-async.ts,shared.tsx, plus a new test file.use-gateway-status.tswas read and reused,not modified. Nothing in
src/app/api/rc/**,src/proxy.ts, other ops panels, orthe RantAIClaw repo was touched.
Changing
use-async.tsaffects every panel that uses it — that is intended, andis why plan 138 depends on this one. No other panel's call sites were changed.