diff --git a/docs/DOM-EDGE-STATES.md b/docs/DOM-EDGE-STATES.md index 14049d3..1ea027a 100644 --- a/docs/DOM-EDGE-STATES.md +++ b/docs/DOM-EDGE-STATES.md @@ -1,6 +1,8 @@ # muse.ai Edge States DOM Reference > Captured live via CDP on `opm` browser (warp-opm netns, CDP 9440) on 2026-10-04. +> Re-verified 2026-10-04 across `muse` (9410), `pip` (9420), `opm` (9440) — +> see §10. No alerts, toasts, approval dialogs, or offline markers on any node. > Method: settled-state probe + `Page.reload` with timed probes at t=0.5/1.5/3/6/10s > to catch transient load states. > Baseline page: `https://muse.ai/thread/2e2c90a1-4b17-4590-b8ba-fb17d8018d36` @@ -128,6 +130,14 @@ auto-generated heartbeat thread title), not the literal `Chats`. The `Back to main chat` button exists this early — it is the S8 escape hatch even during partial load. +**Update 2026-10-04:** the thread-title switcher text is also the *settled*-state +behavior (verified on muse/pip/opm thread pages, e.g. +`646 pip cross-operator coordination chat | Open chat and side chats`). +Never match the switcher by text — use the testid. Likewise, the +`Back to main chat` aria-label button is present on **regular `/thread/` +pages too**, not only during partial load — S8 recovery works from any thread +state, panel open or closed. + ### 2.3 Detection and recovery ```javascript @@ -307,5 +317,34 @@ the earlier DOM investigation. **Never use as signals:** `document.readyState` (already `complete` during S3), `body.innerText.includes('retry'/'loading'/'Side chats')` (false positives -documented above), switcher `aria-expanded` (never set), shimmer +documented above), switcher `aria-expanded` (never set), switcher **inner text** +(it's the thread title, not `Chats`), shimmer `animate-pulse-light` spans (present when settled). + +## 10. Re-verification log (2026-10-04, inspector 5/5) + +Probed `muse`, `pip`, `opm` via CDP `Runtime.evaluate` in each netns: + +| Check | muse | pip | opm | +|---|---|---|---| +| `navigator.onLine` | true | true | true | +| `[role="alert"]` with content | 0 | 0 | 0 | +| Toasts | 0 | 0 | 0 | +| `Allow … to share` in body text | no | no | no | +| Offline/rate-limit markers | none | none | none | +| Typing indicator | absent | absent | absent | +| Composer present | yes | yes | yes | + +Additional observations: +- **Title unread prefix:** pip's title was `(1) Chat — operator-pip` — the title + gains an `(N)` prefix when unreads exist. State detectors should use + `startsWith('Chat —')` on the *end* of the title or strip a leading + `\(\d+\)\s*` before comparing. +- **Shared browsers:** another agent drove pip's browser mid-probe (URL changed + from `/thread/` to `/` and back, panel toggled between probes). + Fleet browsers are a shared resource — re-probe immediately before acting, + and never treat a minutes-old snapshot as current state. +- **Settled-state counts are ranges:** shimmer spans 1 (doc said 2), + `role="status"` 3–4 (doc said 4–7), buttons 46–57 on settled thread pages. + The cheat-sheet's `buttons > 30` threshold still holds; don't hard-code + exact counts. diff --git a/docs/DOM-INDEX.md b/docs/DOM-INDEX.md index c204571..435be73 100644 --- a/docs/DOM-INDEX.md +++ b/docs/DOM-INDEX.md @@ -13,11 +13,20 @@ | Doc | Covers | Last verified | |---|---|---| -| [DOM-CHAT-PANEL.md](./DOM-CHAT-PANEL.md) | Chat panel switcher, compose `+` button, panel shell/tree, thread rows, open-vs-closed detection, 24-test id inventory | 2026-10-04 (CDP, opm) | -| [DOM-MESSAGES.md](./DOM-MESSAGES.md) | Message composer, send/stop buttons, message list (`data-message-id`), chat activation button (`hatch-nav-chat`), loading/empty states | 2026-10-04 ~03:45 UTC (CDP, opm) | -| [DOM-PAGE-STRUCTURE.md](./DOM-PAGE-STRUCTURE.md) | Page states/modes, key selectors, approval dialogs, overlays, URL patterns, 57-testid inventory, automation sequences | 2026-10-04 (CDP, opm) | +| [DOM-CHAT-PANEL.md](./DOM-CHAT-PANEL.md) | Chat panel switcher, compose `+` button, panel shell/tree, thread rows, open-vs-closed detection, 24-testid inventory | 2026-10-04 (CDP, opm) | +| [DOM-MESSAGES.md](./DOM-MESSAGES.md) | Message composer, send/stop buttons, message list (`data-message-id`), chat activation button (`hatch-nav-chat`), loading/empty states | 2026-10-04 ~04:30 UTC (CDP, all 3 nodes) | +| [DOM-PAGE-STRUCTURE.md](./DOM-PAGE-STRUCTURE.md) | Page states/modes, key selectors, nav rail, approval-dialog summary, overlays, URL patterns, testid inventory, automation sequences | 2026-10-04 ~04:35 UTC (CDP, muse/pip/opm) | +| [DOM-EDGE-STATES.md](./DOM-EDGE-STATES.md) | Loading/partial-load (S3), stripped states (S8), empty states, typing indicator, error/toast traps, offline, rate limits, iframes/shadow DOM, readiness cheat-sheet | 2026-10-04 ~04:35 UTC (CDP, muse/pip/opm) | +| [DOM-MESSAGE-ACTIONS.md](./DOM-MESSAGE-ACTIONS.md) | Message actions & thread interactions (`.group/msg`, more-options menus, reactions, reply drafts) | 2026-10-04 ~04:30 UTC (CDP, all 3 nodes) | +| [DOM-NOTIFICATIONS.md](./DOM-NOTIFICATIONS.md) | Notification surface: toast live region (no bell, no notification center), activity indicators | 2026-10-04 (CDP, opm) | +| [DOM-SEARCH.md](./DOM-SEARCH.md) | Global search / command palette: trigger, input, results | 2026-10-04 (CDP, opm) | +| [DOM-SETTINGS.md](./DOM-SETTINGS.md) | Settings, profile & dock rail structure (no credential values recorded) | 2026-10-04 (CDP, opm) | | [DOM-APPROVALS-SPEC.md](./DOM-APPROVALS-SPEC.md) | Approval/permission dialog detection, classification (trusted IPs), handling, exit-code contract (`2` = `APPROVAL_NEEDED`) | 2026-10-03 | +> Naming note: inspector 3/5's brief mentioned `DOM-APPROVALS.md`; the file on +> disk is `DOM-APPROVALS-SPEC.md` (since 2026-10-03). This index references the +> actual filename. + **Cross-reference guide:** - Sidechat creation mechanics → CHAT-PANEL (§4 safe toggle, §6 automation notes) - What to type into / how to send → MESSAGES (§1 composer, §2 send) @@ -35,7 +44,8 @@ | testid | Tag | Purpose | Docs | |---|---|---|---| -| `hatch-chat-switcher-trigger` | `BUTTON` | Panel open/close **toggle**. Text: `Chats`. Never blind-click — check compose first | C, P | +| `hatch-chat-switcher-trigger` | `BUTTON` | Panel open/close **toggle**. Inner text is the **current thread title** (not `Chats`) — never match by text. Never blind-click — check compose first | C, P | +| `hatch-side-chats-open-drag-handle-divider-line` | `DIV` | Divider for the closed-panel drag handle; absent when panel is open (verified 2026-10-04, all nodes) | P | | `hatch-chat-switcher-icon-knockout` | `SPAN` | Icon wrapper inside switcher | C, P | | `hatch-chat-switcher-navigation-icon` | `SPAN` | Hamburger SVG inside switcher | C, P | | `hatch-chat-leading-controls` | `DIV` | Top-left container holding the switcher | C, P | @@ -63,7 +73,7 @@ | testid | Tag | Purpose | Docs | |---|---|---|---| | `hatch-dock-rail` | `DIV` | Left dock rail container | C, M, P | -| `hatch-dock-rail-divider` | `DIV` | Rail divider | P | +| `hatch-dock-rail-divider` | `SPAN` | Rail divider | P | | `hatch-nav-chat` | `A` | **Chat section link** (`href="/"`). `aria-label` is dynamic (`Chat` / `Chat, N notifications`) — match testid, never exact aria-label. `aria-current="page"` when Chat active. **The reliable chat activator** | C, M, P | | `hatch-dock-chat-notification-badge` | — | Notification badge on nav-chat | P | | `hatch-nav-search` | `BUTTON` | Search nav | C, M, P | @@ -91,6 +101,7 @@ | `hatch-status-panel-close-toolbar` | `DIV` | Toolbar | P | | `hatch-invite-friends-button` | `BUTTON` | Invite (`aria-label="Invite"`) | C, P | | `hatch-browser-task-banner-host` | `DIV` | Browser task banner (hidden) | C, P | +| `hatch-inline-approval-card` | `DIV` | Browser-task approval card (`A task needs review` + `Review` button), no IP in text. Structure TBD — inspector 3/5, DOM-APPROVALS-SPEC.md | P | --- @@ -103,7 +114,7 @@ | `#hatch-chat-scroll` | Message scroll container | M | | `div[role="log"][aria-label="Chat messages"]` | Message list | M | | `[data-message-id]` | Each message. `assistant-msg-` = assistant, bare `` = user | M | -| `button[aria-label="Back to main chat"]` | **Escape hatch from `/thread/new`** stripped state (empty text, match aria-label) | *(subagent report 2026-10-04 — not yet in a detail doc)* | +| `button[aria-label="Back to main chat"]` | **Primary main-chat affordance** from any thread state (`/thread/` and `/thread/new`), panel open or closed. Verified 2026-10-04 on all 3 nodes | P (§2.7) | | Exact-text `Main chat` element | Main-chat navigation target — match `(e.innerText).trim() === 'Main chat'` on `button, [role="button"]`, never substring | P (§2.7), AGENTS.md | --- @@ -201,18 +212,23 @@ Text-based (no stable testids observed). See APPROVALS-SPEC for the full contrac | 2 | PAGE-STRUCTURE §1.3 "Recovery: Send Ctrl+J" from `/thread/new` | **Use `button[aria-label="Back to main chat"]` click** or `hatch-nav-chat` click instead | | 3 | `/thread/new` title: MESSAGES §5 and PAGE-STRUCTURE §1.3 say `Muse`; a 2026-10-04 probe table said `Chat — …` | **Docs consensus: `Muse`.** Treat title as advisory only — detect via URL + switcher absence (§4b), never title alone | | 4 | "Sidechat create navigates to main first" (older code comment) | **Unnecessary.** The ready state for *create* is any chat-active view; `hatch-nav-chat` recovery lands on main anyway | +| 5 | CHAT-PANEL / older notes describe switcher text as `Chats` (or `Chats\nUnread chats…`) | **Thread title.** Verified 2026-10-04 on all nodes: switcher innerText = `\nOpen chat and side chats`. Never match the switcher by text | --- ## 6. Gaps — Unmapped Territory -1. **`button[aria-label="Back to main chat"]`** — verified in live probes, referenced in §3, but has no dedicated section in any detail doc yet. +1. **`hatch-inline-approval-card` structure** — testid + surface text documented (PAGE-STRUCTURE §6, this index §2), full structure deferred to inspector 3/5 in DOM-APPROVALS-SPEC.md. 2. **Feed / Ideas / Goals / Library pages** — dock testids listed, page content unmapped. -3. **Search overlay** (`hatch-nav-search` target) — unmapped. +3. **Search overlay** (`hatch-nav-search` target) — trigger documented in DOM-SEARCH.md; result-row interactions unmapped. 4. **OTP / account-selection states** — body-text markers known (`To log in, enter the code`, `Your email matches multiple accounts`), DOM unmapped. Automation should abort to human here. 5. **`cmd_sidechat_main`'s exact-text `Main chat` lookup** — documented in AGENTS.md and §3/§4e here, no DOM-doc section. 6. **Partial-load state** (title `Muse`, ~21 buttons, transient) — mentioned in MESSAGES §5, no recovery procedure beyond "navigate to `/` and re-probe". 7. **Message read-back verification selectors** — MESSAGES §3 covers extraction; the `dm.py` tail-match verification loop is code-only. +8. **`/tmp` script collisions between inspectors** — two inspectors overwrote each other's `/tmp/cdp_eval.py` on bl mid-task (2026-10-04). Namespace throwaway scripts per-inspector (e.g. `/tmp/cdp_eval_.py`). + +> Resolved 2026-10-04: `button[aria-label="Back to main chat"]` now has a +> dedicated section in PAGE-STRUCTURE §2.7 (was gap #1 in the previous index). --- @@ -222,3 +238,8 @@ Text-based (no stable testids observed). See APPROVALS-SPEC for the full contrac - Contradiction rule: **newer verified probe beats older doc text**; record the resolution in §5 with the commit or date. - "Last verified" in §1 is per-doc. If you re-probe a doc's selectors, bump its date. - Prefer adding a dated note over rewriting history — these docs are also an audit trail. +- 2026-10-04 regeneration: document map now covers all 10 `DOM-*.md` files + (was 4); master table carries the two new testids + (`hatch-side-chats-open-drag-handle-divider-line`, `hatch-inline-approval-card`) + and the switcher-text + dock-rail-divider corrections. When a new DOM doc + lands, add its row to §1 and its testids to §2 in the same commit. diff --git a/docs/DOM-PAGE-STRUCTURE.md b/docs/DOM-PAGE-STRUCTURE.md index 4a6a0e0..d4b8451 100644 --- a/docs/DOM-PAGE-STRUCTURE.md +++ b/docs/DOM-PAGE-STRUCTURE.md @@ -88,7 +88,11 @@ const isThreadNew = !document.querySelector('[data-testid="hatch-chat-switcher-trigger"]')); ``` -**Recovery:** Send Ctrl+J via CDP to return to main chat, then open panel via switcher click. +**Recovery:** Click `button[aria-label="Back to main chat"]` (present on `/thread/new` +and on regular `/thread/` pages — verified on all fleet nodes 2026-10-04), +or click `[data-testid="hatch-nav-chat"]`, then open the panel via the switcher. +Do NOT use Ctrl+J here: stripped states lack keyboard focus, so the keystroke +goes nowhere (supersedes earlier Ctrl+J guidance; see DOM-INDEX §5). **Distinct testid set** (no chat-switcher testids, dock-rail based): ``` @@ -146,7 +150,7 @@ document.querySelector('[data-testid="hatch-chat-switcher-trigger"]') |---|---| | Tag | `BUTTON` | | `data-testid` | `hatch-chat-switcher-trigger` | -| Inner text | `Chats\nOpen chat and side chats` (or `Chats\nUnread chats\nOpen chat and side chats` when unreads exist) | +| Inner text | **The current thread's title** (e.g. `646 pip cross-operator coordination chat\nOpen chat and side chats`) — **NOT** the literal `Chats`. Never match the switcher by text. | | `aria-expanded` | Not used (always `null`) | | Class (partial) | `bg-fill-blur-thick shadow-blur-elevation-01 text-body-medium` | @@ -271,25 +275,33 @@ TEXTAREA[aria-label="Message"] └── DIV (cls: backdrop-blur-elevation-01 bg-fill-blur-thick shadow-blur-elevation-01) ``` -### 2.7 "Main Chat" Navigation Button +### 2.7 "Main Chat" Navigation -To navigate to main chat from a sidechat, find the button with **exact** text `"Main chat"`: +**Primary (verified 2026-10-04 on muse/pip/opm):** the button with +`aria-label="Back to main chat"` — present on `/thread/` pages and on +`/thread/new`, even with the panel closed: + +```javascript +const backBtn = [...document.querySelectorAll('button')] + .find(b => b.getAttribute('aria-label') === 'Back to main chat'); +backBtn?.click(); +``` + +**Panel-open alternative:** when the sidechat panel is open, find the element with +**exact** text `"Main chat"`: ```javascript const mainBtn = [...document.querySelectorAll('button, [role="button"]')] .find(e => (e.innerText || '').trim() === 'Main chat'); ``` -**Must use exact match** (`=== 'Main chat'`), not substring — thread rows may contain "Main chat" as part of longer text. +**Must use exact match** (`=== 'Main chat'`), not substring — thread rows may contain +"Main chat" as part of longer text. Note: with the panel **closed** on a thread +page there is no exact-text "Main chat" element at all — use the aria-label +button above. -**Alternative:** Ctrl+J via CDP `Input.dispatchKeyEvent` (proven reliable for main-chat navigation): -```javascript -// Via CDP websocket: -{"id": 30, "method": "Input.dispatchKeyEvent", - "params": {"type": "keyDown", "key": "j", "code": "KeyJ", "ctrlKey": True, "modifiers": 2}} -{"id": 31, "method": "Input.dispatchKeyEvent", - "params": {"type": "keyUp", "key": "j", "code": "KeyJ", "ctrlKey": True, "modifiers": 2}} -``` +**Do not use Ctrl+J** for this (superseded 2026-10-04): it needs keyboard focus, +which stripped/partial states lack. Prefer DOM clicks (commit `72574ba`). --- @@ -430,15 +442,25 @@ hatch-side-chats-panel-sliding-surface hatch-side-chats-panel-content hatch-side-chats-options hatch-side-chat-unread-indicator +hatch-side-chats-open-drag-handle # present when panel CLOSED (verified 2026-10-04) +hatch-side-chats-open-drag-handle-divider-line # NEW 2026-10-04: divider for the above; absent when panel open hatch-thread-row # Individual chat rows (× N) active-side-chat-load-more-sentinel ``` ### Navigation Dock ``` -hatch-dock-rail +hatch-dock-rail # DIV; children: dock-rail-divider (SPAN), + # nav-chat, dock-chat-notification-badge, + # nav-search, 4× nav-system-item-, + # app-download-dock-mark, dock-more +hatch-dock-rail-divider # SPAN (not DIV) hatch-dock-more -hatch-nav-chat +hatch-nav-chat # A, href="/", aria-label dynamic: + # "Chat" | "Chat, N notification(s)" — + # match testid, NEVER exact aria-label. + # aria-current="page" when Chat section active. + # Verified 2026-10-04 on muse/pip/opm. hatch-nav-search hatch-nav-system-item-66656564 # (hex-encoded IDs) hatch-nav-system-item-676f616c73 @@ -456,6 +478,9 @@ hatch-status-panel-close-toolbar hatch-status-panel-sliding-surface hatch-composer-placeholder-overlay hatch-browser-task-banner-host +hatch-inline-approval-card # NEW 2026-10-04 (pip): DIV, no role — + # browser-task approval card ("A task needs review" + Review button), + # no IP in text. Structure TBD — see DOM-APPROVALS-SPEC.md (inspector 3/5). hatch-invite-friends-button app-download-dock-mark ``` @@ -467,9 +492,14 @@ app-download-dock-mark ### 7.1 Sidechat Creation (Robust) ```python -# 1. Navigate to main chat (recover from any state) -send_ctrl_j() # CDP Input.dispatchKeyEvent -time.sleep(3) +# 1. Recover to a chat-active state (click-based; never Ctrl+J — see §2.7): +# priority: compose (already ready) -> switcher -> hatch-nav-chat +# (muse-chat-api.py cmd_sidechat_main clicks the exact-text "Main chat" +# element; the aria-label "Back to main chat" button also works) +# Poll up to ~20s for the compose button. +# NOTE: plain JS .click() on the switcher does NOT reliably toggle the panel +# (Radix control — trusted CDP Input.dispatchMouseEvent needed, verified +# 2026-10-04). Prefer the compose-button presence check over blind toggling. # 2. Verify we're on main chat assert window.location.href == 'https://muse.ai/' @@ -536,3 +566,12 @@ btn.click() ## Appendix: Change Log - **2026-10-04:** Initial mapping via live CDP inspection. Documented 5 page states, 57 testids, approval dialog logic, and the `/thread/new` poisoning behavior. +- **2026-10-04 (inspector 5/5 re-verification, muse/pip/opm via CDP):** + - `hatch-nav-chat` verified: `A`, `href="/"`, dynamic `aria-label` (`Chat` / `Chat, N notification(s)`), `aria-current="page"`. + - Switcher inner text is the **current thread title**, not `Chats` — corrected §2.1 (never match switcher by text). + - `button[aria-label="Back to main chat"]` is present on `/thread/` pages too, not only `/thread/new` — §2.7 rewritten around it as the primary main-chat affordance; Ctrl+J guidance removed as superseded. + - Plain JS `.click()` on the switcher does **not** reliably toggle the panel (Radix control needs trusted CDP pointer events) — noted in §7.1. + - New testids: `hatch-side-chats-open-drag-handle-divider-line` (all nodes), `hatch-inline-approval-card` (pip; browser-task approval card, no IP — structure deferred to DOM-APPROVALS-SPEC.md). + - Title gains an unread prefix: `(1) Chat — ` (observed on pip). + - Settled-state variance: shimmer spans 1 (not 2), `role="status"` 3–4 (not 4–7), buttons 46–57 — ranges, not fixed counts. + - Fleet browsers are shared: another agent drove pip's browser mid-probe (URL/panel state changed between probes). Never assume a probe snapshot is still current — re-probe before acting.