Repository navigation
fix(p0): debug-session batch 1 — WS URL, playwright 500, base.css import - #6718
Conversation
|
| 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:
- ✅
7ec7961f7— playwrightbrowser_connectednull coercion - ✅
c7f7b9385— base.css import in main.ts - ✅
175cb33e1— orchestration.py post-refactor(orchestrator): three overlapping workflow execution paths in single Orchestrator class #5058 collaborator routing - ✅
545afe7d0— AbortError suppression in errorHandler.ts
```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?
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>
…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>
…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>
…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>
✅ SSOT Configuration Compliance: Passing🎉 No hardcoded values detected that have SSOT config equivalents! |
…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>
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 theupgrade falls through to the SPA HTML and Chrome reports "WebSocket error:
Event" every 2-8 s. Changed to
/ws/liveto match the backend handler atapi/live_events.py:125(router mounted at empty prefix incore_routers.py:311); nginx already proxies/ws*(location /wsblock).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 isattached, but
PlaywrightWorkerStatusResponse.browser_connectedis anon-nullable
bool. FastAPI raisedResponseValidationErroron every call→ 500 → empty BrowserAutomationView panel.
Normalized the response in the handler:
bool(None) == False, statusfallback to
"unknown", page_open kept Optional[bool].3. base.css never imported (root-cause finding C)
src/assets/base.csscontains the global reset (* { margin:0; padding:0; box-sizing:border-box },body { margin:0; min-height:100vh }) but wasnot imported by
main.ts. Served bundle had zero globalbody{margin:0},only Vue-scoped
body[data-v-XXX]rules and leaked unscoped rules fromcomponent 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 viewswithout
.view-containerhad no scroll wrapper.Added
import './assets/base.css'after vue-notus and design-system CSSso its global reset wins on equal-specificity selectors.
Test plan
/live→/ws/live)/ws/liveconfirmed registeredlocation /wsproxy confirmedcurl /api/playwright/worker-statusreturns HTTP 200 withbrowser_connected: falseFollow-ups (separate issues)
@with_error_handlingdecorators inapi/playwright.py:549/555(cosmetic dead-code from fix(api): 23 stacked-duplicate @with_error_handling decorators across 9 files (codebase-wide #6501 follow-up) #6633 cleanup)admin(security audit)services/slm_client.py:565builds/api/ws/events— non-existent path; 568+/5MB log spamansible_default_ipv4.addressusages (WSL2 fact instability)body{...}rules from packages (5 distinct rules in bundle) — separate cleanup pass🤖 Generated with Claude Code