Skip to content

fix(p0): debug-session batch 1 — WS URL, playwright 500, base.css import - #6718

Merged
mrveiss merged 5 commits into
Dev_new_guifrom
debug-batch-p0
May 2, 2026
Merged

mrveiss merged 5 commits into
Dev_new_guifrom
debug-batch-p0

Conversation

@mrveiss

@mrveiss mrveiss commented May 1, 2026

Copy link
Copy Markdown
Owner

Summary

Three P0 fixes from a /superpowers:systematic-debugging session driven by
user-reported symptoms ("can't access https://172.16.168.20/", "divs do not
have content", "page not full page", "divs with longer content missing
scrolling") plus a Chrome-plugin debug TODO.

Fixes

1. LiveEventService WebSocket URL (Chrome-TODO #1, cascades to #2 #3)

Frontend was building wss://host/live — no nginx location matches, so the
upgrade falls through to the SPA HTML and Chrome reports "WebSocket error:
Event" every 2-8 s. Changed to /ws/live to match the backend handler at
api/live_events.py:125 (router mounted at empty prefix in
core_routers.py:311); nginx already proxies /ws* (location /ws block).

This single-line fix also unblocks chat (which streams replies through this
WS) and Workflow Builder reconnect loops.

2. /api/playwright/worker-status 500 (root-cause finding A)

Browser VM (http://127.0.0.1:9001/status) returns
{"status":"disconnected","browser_connected":null,...} when no session is
attached, but PlaywrightWorkerStatusResponse.browser_connected is a
non-nullable bool. FastAPI raised ResponseValidationError on every call
→ 500 → empty BrowserAutomationView panel.

Normalized the response in the handler: bool(None) == False, status
fallback to "unknown", page_open kept Optional[bool].

3. base.css never imported (root-cause finding C)

src/assets/base.css contains the global reset (* { margin:0; padding:0; box-sizing:border-box }, body { margin:0; min-height:100vh }) but was
not imported by main.ts. Served bundle had zero global body{margin:0},
only Vue-scoped body[data-v-XXX] rules and leaked unscoped rules from
component packages
(body{padding-inline:calc(var(--spacing) * 6); flex:auto},
etc.). Combined with default 8px browser body margin, the
<div id="app" class="h-screen"> was pushed off-viewport, and views
without .view-container had no scroll wrapper.

Added import './assets/base.css' after vue-notus and design-system CSS
so its global reset wins on equal-specificity selectors.

Test plan

  • LiveEventService.ts diff is one character (/live → /ws/live)
  • Backend route /ws/live confirmed registered
  • Nginx location /ws proxy confirmed
  • Local repro of normalized playwright response passes Pydantic schema
  • After deploy: WebSocket connects without console errors; chat streams replies
  • After deploy: curl /api/playwright/worker-status returns HTTP 200 with browser_connected: false
  • After frontend rebuild + deploy: page fills viewport edge-to-edge, long content scrolls

Follow-ups (separate issues)

🤖 Generated with Claude Code

@mrveiss

mrveiss commented May 2, 2026

Copy link
Copy Markdown
Owner Author

⚠️ Held back: LiveEventService.ts URL change builds double /ws/

Spotted while reviewing for merge. The LiveEventService change (132023acd) flips the URL from \${websocketUrl}/live to \${websocketUrl}/ws/live, but config.websocketUrl already includes /api/ws — see ssot-config.ts:456-470:

```ts
get websocketUrl(): string {
...
if (isHttps) return `wss://${host}/api/ws`;
return `ws://${vm.main}:${port.backend}/api/ws`;
}
```

That gives:

code URL built matches backend /api/ws/live?
\${websocketUrl}/live (current Dev_new_gui) wss://host/api/ws/live ✅ yes
\${websocketUrl}/ws/live (this PR) wss://host/api/ws/ws/live ❌ 404

History on the getter — #6271 ("unify WebSocket URL to /api/ws") and #6232 confirm /api/ws has been baked into websocketUrl since Phase 1.

The Chrome 'WebSocket error' you observed was almost certainly the auth-token issue from #6692 (fixed in #6705, already merged to Dev_new_gui). After #6705, \${websocketUrl}/live should connect cleanly with the JWT — the URL itself was always right.

Proposed fix

Drop commit 132023acd and rebase. The other 4 commits in this PR are independently good:

```bash
git rebase -i origin/Dev_new_gui # drop the 132023a line
git push -f origin debug-batch-p0
```

Or revert just that one commit:

```bash
git revert 132023a
git push origin debug-batch-p0
```

Want me to push the revert and re-merge?

mrveiss and others added 5 commits May 2, 2026 09:49
Frontend was building wss://host/live - which has no nginx location
rule (falls through to SPA index.html, breaks the WS upgrade with 404).
Backend handler is registered at /ws/live (api/live_events.py:125,
router mounted at empty prefix in core_routers.py:311), and nginx
already proxies /ws/* to the backend (location /ws block in
sites-enabled/autobot).

This is the root cause behind:
- Repeating LiveEventService 'WebSocket error: Event' in console
- Chat reply never streamed back (UI stuck on Loading)
- Workflow Builder WebSocket reconnect loops
- 568+/5MB backend WS-403 spam (slm_client also misconfigured;
  separate fix)

Tested by: confirming /ws/live is the registered route via
`grep -rn '@router.websocket' autobot-backend/api/live_events.py`
and that nginx 'location /ws { proxy_pass http://autobot_backend; }'
exists in /etc/nginx/sites-enabled/.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…/worker-status

Browser VM (BROWSER_VM_URL/status) returns null for browser_connected
when no browser session is attached, e.g.:
  {"status":"disconnected","browser_connected":null,"page_open":null,"current_url":null}

But api/schemas_code.py:822 declares browser_connected as a non-nullable
bool. The handler at api/playwright.py:560 was returning the upstream
JSON verbatim, so FastAPI raised ResponseValidationError on every call,
producing HTTP 500 and an empty BrowserAutomationView panel.

Fix: normalize the response in the handler so browser_connected is
always a bool (None -> False, falsy -> False). page_open keeps its
Optional[bool] semantics. Status falls back to 'unknown' if upstream
omits it.

Verified locally: passing the real upstream payload through the new
normalization yields a dict that PlaywrightWorkerStatusResponse
validates without raising.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…t layout

Symptoms:
- Page does not fill the viewport (visible margin around content)
- Long-content divs don't scroll
- Some panels appear empty (content overflows out of view, no scrollbar)

Root cause: src/assets/base.css contains the foundational reset
(`* { margin:0; padding:0; box-sizing:border-box }`,
`body { margin:0; min-height:100vh }`) but was never imported by
main.ts. It is referenced only by src/assets/main.css (`@import './base.css'`),
which itself is also never imported.

Verified by inspecting served bundle (/assets/index-WdD47OxA.css):
zero global `body { margin:0 }` or `html` rules. Instead, leaked
unscoped rules from packages and component CSS add body padding
(`body{padding-inline:calc(var(--spacing) * 6);padding-block:calc(var(--spacing) * 4);flex:auto}`),
combined with the default 8px browser body margin, push the
`<div id="app" class="h-screen">` outside the viewport.

Fix: `import './assets/base.css'` from main.ts AFTER all other
foundation CSS (tailwind, vue-notus, theme, view, design-system) so
its `*` reset and explicit `body { margin:0; min-height:100vh }`
take precedence on equal-specificity selectors.

The deeper cleanup of leaked unscoped body{...} rules in component
packages will be a separate audit pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… collaborators

Endpoint returned HTTP 500 with body "Failed to get capabilities".
Backend log: 'Orchestrator' object has no attribute '_calculate_capability_coverage'.

In #5058 the Orchestrator god-class was decomposed:
- capability_coverage moved to AgentRouter.calculate_capability_coverage()
  (reachable via orchestrator._runner.get_performance_report())
- agent_performance moved to PerformanceTracker.agent_performance
  (reachable via orchestrator._perf.agent_performance)

The /capabilities handler in api/orchestration.py was not updated for
the refactor and still called the removed Orchestrator methods/attrs:
- orchestrator._calculate_capability_coverage()  (gone)
- orchestrator.agent_performance[agent]          (gone)

Fix: route through the collaborators using getattr fallbacks so the
handler degrades gracefully if either is unavailable instead of 500-ing.

After fix, the endpoint returns:
{capability_coverage: {...}, agents: {...}, total_agents: N}

instead of HTTP 500. This was the cause of "Failed to load agent
capabilities" errors surfaced by useWorkflowBuilder in the frontend.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Components that abort in-flight requests on unmount or rapid navigation
(e.g. resource.abort() in useFetchEndpoint) cause [errorHandler] Fetch
error: AbortError to spam the console even though the abort was
intentional and not a real failure.

Aligned with the same suppression already done in
useFetchEndpoint.ts:263-266 (`(err instanceof DOMException && err.name
=== 'AbortError') || (err as Error)?.name === 'AbortError'`).

The check joins the existing health-check suppression at line 141.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@mrveiss
mrveiss merged commit 621fcf9 into Dev_new_gui May 2, 2026
2 of 4 checks passed
@mrveiss
mrveiss deleted the debug-batch-p0 branch May 2, 2026 06:49
mrveiss added a commit that referenced this pull request May 2, 2026
…on't abort Phase 0 (#6719)

Same root cause and fix as #6720 for the nginx role. The fleet
provisioning playbook still aborts at Phase 0 on the local SLM
Manager — this time at 'nodejs | Install Node.js' with the same
'Failed to update apt cache: unknown reason' after a ~5-minute
hang on 'Check if NodeSource repo exists' (apt's PPA refresh is
the slow step there).

The mitigation pattern from #6720 applies verbatim: cache_valid_time
makes the apt module skip the refresh when the cache is fresh (<=1h),
so a single flaky third-party repo no longer takes down the whole
provisioning run.

Verified by reading the playbook log: provisioning was at the merged
commit 621fcf9 but couldn't deploy any of the 5 fixes from #6718
because Phase 0 failure short-circuits Phase 4a (Backend) and Phase 4b
(Frontend) — backend service never restarts, frontend bundle never
rebuilds.

Tracked under the codebase-wide audit umbrella #6719 (~40 remaining
update_cache: true callsites).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mrveiss added a commit that referenced this pull request May 2, 2026
…on't abort Phase 0 (#6719) (#6726)

Same root cause and fix as #6720 for the nginx role. The fleet
provisioning playbook still aborts at Phase 0 on the local SLM
Manager — this time at 'nodejs | Install Node.js' with the same
'Failed to update apt cache: unknown reason' after a ~5-minute
hang on 'Check if NodeSource repo exists' (apt's PPA refresh is
the slow step there).

The mitigation pattern from #6720 applies verbatim: cache_valid_time
makes the apt module skip the refresh when the cache is fresh (<=1h),
so a single flaky third-party repo no longer takes down the whole
provisioning run.

Verified by reading the playbook log: provisioning was at the merged
commit 621fcf9 but couldn't deploy any of the 5 fixes from #6718
because Phase 0 failure short-circuits Phase 4a (Backend) and Phase 4b
(Frontend) — backend service never restarts, frontend bundle never
rebuilds.

Tracked under the codebase-wide audit umbrella #6719 (~40 remaining
update_cache: true callsites).

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
mrveiss added a commit that referenced this pull request May 2, 2026
…6727)

#6718 changed `${config.websocketUrl}/live` → `${config.websocketUrl}/ws/live`
under the assumption that `websocketUrl` was bare (`wss://host`). It isn't.
Per the getter in ssot-config.ts and #6271 ("unify WebSocket URL to /api/ws"),
`websocketUrl` already ends in `/api/ws` — so the change builds
`wss://host/api/ws/ws/live`, which 404s every connection.

Reverting to the original `/live` suffix. Final URL is `wss://host/api/ws/live`,
which matches the backend handler at api/live_events.py:125 (router included
with empty prefix in core_routers.py:311, FastAPI app prefix /api).

The Chrome 'WebSocket error: Event' that motivated #6718's URL change was the
auth-token issue from #6692 (fixed by #6705 already on Dev_new_gui). The URL
itself was always correct; only the missing JWT was the problem. Adding a
stale comment to the getUrl helper to prevent the same mis-fix recurring.

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@github-actions

github-actions Bot commented May 2, 2026

Copy link
Copy Markdown
Contributor

✅ SSOT Configuration Compliance: Passing

🎉 No hardcoded values detected that have SSOT config equivalents!

mrveiss added a commit that referenced this pull request May 2, 2026
…race, Agent Registry tabs (#6749)

* fix(ansible/redis): redis_exporter target 127.0.0.1 instead of brittle ansible_default_ipv4

Phase 5 'Redis Stack Configuration' summary on the SLM Manager prints:
  Redis Host: 192.168.168.21

That IP exists on no interface of the host (current interfaces are
192.168.1.101 and 172.16.168.20). Result: redis_exporter floods journald
every ~14 s with 'Couldn't connect to redis instance (redis://192.168.168.21:6379)'
(20+ errors per 5 min observed on a fresh deploy), and Prometheus loses
all Redis metrics.

Root cause: ansible_default_ipv4.address is brittle on WSL2 / multi-NIC
hosts where transient routing changes cause Ansible to record a stale
fact. The redis_exporter and redis-stack-server always run on the same
host (the redis role wires both up co-located), so the connection target
should be the loopback address — no fact dependency, no breakage when
the default route flips.

This is the targeted fix for redis_exporter only. The ~20 other
ansible_default_ipv4.address callsites (frontend.env.j2, redis-backup.sh.j2,
slm-secrets.env.j2, etc.) are tracked under the broader audit umbrella
and may need different replacements depending on whether the value is
used for the local host vs a service-discovery target.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(frontend): guard ApexCharts heatmap against empty initial series — fixes /home race

User-reported symptom on /home dashboard:
  [RumAgent] CRITICAL ISSUE: unhandled_promise_rejection
  Element not found at vue3-apexcharts-*.js:833
  → "Application Error - Element not found" toast

Root cause: useResourceMetrics composable initialises heatmapData to
`ref<HeatmapSeries[]>([])`. The ResourceHeatmap template's existing
v-if/v-else-if/v-else gate handles loading and error states, but
hands an empty series to <apexchart> on the first paint after the
loading flag flips. vue3-apexcharts then creates an ApexCharts instance
in onMounted, which immediately tries to find the SVG container row
that an empty series never produces — hence "Element not found".

Fix: gate the <apexchart> with v-if="chartSeries.length > 0" and
render a no-data placeholder with the same dashed-style frame the
loading/error states already use. Behaviour:
- empty series   → no-data placeholder, no chart instance, no race
- non-empty      → chart mounts as before
- transition     → Vue patches the placeholder→chart cleanly because
                    chartSeries.length flips after data lands

Added i18n key visualizations.resourceHeatmap.noData (English only;
other locales fall back to English until translated — this is the
existing convention in this codebase).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(frontend): use gap-8 not space-x-8 for Agent Registry tabs

After PR #6718 imported base.css (the global `* { margin: 0; padding: 0 }`
reset), Tailwind's `space-x-*` utilities still win on specificity in
isolation but become brittle in nested contexts: any descendant rule that
re-targets margins on children of a flex container can now flip the
result. Easier and visually identical: use `gap-8` on the flex
container, which is a parent layout property immune to the `*` reset.

User symptom: header showed "Backend Agents (29)Specialized Agents (0)Settings"
with no whitespace between the three tab buttons.

This change only swaps the spacing utility on a single nav element. The
broader audit of `space-x-*` usage across views/components is tracked
as a discovery (~50 callsites; many are fine, but worth a sweep to
prefer gap-* for new code in this codebase).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant