chore(overseer): structured log on soft brain-unavailable converse - #111
chore(overseer): structured log on soft brain-unavailable converse#111heavygee wants to merge 3 commits into
Conversation
Keep HTTP 200 + friendly reply, but emit a greppable journal line so headset/GPU offline episodes are visible without digging convo_turn payloads. Co-authored-by: Cursor <cursoragent@cursor.com>
The offline reply should stay generic — root cause is often DNS/config, not GPU-for-VR, and the VR aside was misleading. Co-authored-by: Cursor <cursoragent@cursor.com>
Debug converse was hard-defaulting profile=default, which ignored a working saved local brain and hit a dead MagicDNS URL. Co-authored-by: Cursor <cursoragent@cursor.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6498a482c7
ℹ️ About Codex in GitHub
Your team has set up Codex to 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 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // the operator saved a working profile (e.g. local loopback). | ||
| if (res.active?.profile) { | ||
| setSelectedProfile(res.active.profile) | ||
| setSelectedModel(res.active.model ?? '') |
There was a problem hiding this comment.
Preserve the active model when applying the active profile
When the persisted active profile differs from the initial default profile and has a model override, changing selectedProfile here reruns the model-list effect, which unconditionally resets selectedModel to ''. The next converse request still supplies the profile but omits the saved model; resolveBrainSelection treats an explicit profile as a wholesale override, so the conversation uses the profile's default model instead of the operator's active model.
Useful? React with 👍 / 👎.
| reachable: error.reachable, | ||
| status: error.status ?? null, | ||
| model: config.model, | ||
| profile: parsed.data.profile ?? null, |
There was a problem hiding this comment.
Log the effective profile instead of only the request override
When a valid caller omits profile to use the persisted active brain and that brain request fails, this new warning records profile: null even though config was resolved from active.profile. That makes the diagnostic line ambiguous precisely for the default API flow and can send operators toward the wrong endpoint or credentials; retain the resolved selection and log its effective profile here.
Useful? React with 👍 / 👎.
| })) | ||
| if (!config) { | ||
| // Soft 200 for clients — journal still needs a greppable line (access log is 200). | ||
| console.warn('[Overseer][Converse] brain unavailable', { |
There was a problem hiding this comment.
Keep structured warning fields on the greppable line
Under Bun, passing this object as a second console.warn argument renders the warning across multiple lines: the prefixed line contains only [Overseer][Converse] brain unavailable {, while reason, kind, reachable, and the other fields appear on separate unprefixed lines. Consequently, grepping the journal for the advertised prefix returns none of the diagnostic fields, defeating the purpose of this structured warning; serialize the payload onto the prefixed line or use a logger that emits a single structured record.
Useful? React with 👍 / 👎.
| // Reachable-but-failed (http 4xx/5xx, malformed body) is a converse | ||
| // bug, not an offline brain — do not mislabel it as GPU/VR downtime. | ||
| // Soft 200 for clients — structured warn so journalctl can find it. | ||
| console.warn('[Overseer][Converse] brain unavailable', { |
There was a problem hiding this comment.
Warn when the brain fails after a successful write
When a follow-up brain call fails after ping_session or another write succeeds, runOverseerConverse deliberately catches the BrainUnavailableError in hub/src/overseer/converse.ts and returns a fallback success so the operator does not repeat the write. The route therefore resolves normally and never reaches this warning, leaving that soft brain outage absent from the journal; carry failure metadata back in the result or emit the warning where the error is swallowed while retaining the successful-write fallback.
Useful? React with 👍 / 👎.
Summary
not_configured/BrainUnavailableError) still return HTTP 200 + friendly reply + persistedconvo_turn(client UX unchanged).console.warn('[Overseer][Converse] brain unavailable', { reason, kind, reachable, ... })sojournalctl -u hapi-hub-ooscan find headset/GPU offline episodes without grepping DB payloads.Test plan
bun test hub/src/web/routes/overseer.test.ts(14 pass)[Overseer][Converse] brain unavailablewhile UI still shows soft offline copyIssues
Fixes #110
Made with Cursor