DOM inspector 5/5: verify page structure + edge states, regenerate index
Session: sidechat/dom-structure
This commit is contained in:
+40
-1
@@ -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/<uuid>`
|
||||
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/<uuid>` 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.
|
||||
|
||||
+29
-8
@@ -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-<uuid>` = assistant, bare `<uuid>` = 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/<uuid>` 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 = `<current thread title>\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_<area><n>.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.
|
||||
|
||||
+57
-18
@@ -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/<uuid>` 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/<uuid>` 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-<hex>,
|
||||
# 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/<uuid>` 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 — <agent>` (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.
|
||||
|
||||
Reference in New Issue
Block a user