Files
box/docs/HATCH-MENU.md
T

4.7 KiB

hatch_menu — Modular Settings-Menu Navigation + Toggles

bin/hatch_menu/ drives the muse.ai Settings menu in the agent browsers. One module per part: a site change means patching one file. Exposed as box chromebox permissions ...; bin/invite.py usage reads ride the same tree.

Layout

bin/hatch_menu/
  __init__.py      package surface (dialog + mouse + toggles fns)
  mouse.py         trusted input: real_click, escape, close, MouseError
  dialog.py        open_settings (retried), goto_tab, click_row,
                   describe_rows, go_back, dialog_text, TAB_NAMES
  controls.py      radio/switch list + set (verify, else trusted
                   click, verify again)
  toggles.py       toggle registry + sessions: resolve/get/set/list,
                   describe_tab, MenuError
  tabs/
    __init__.py    TAB_MODULES (10 tabs, uniform describe(ws))
    permissions.py defaults radios, website modes, protocols,
                   advanced switches, counts, tasks
    general.py     usage parse, theme, redeem entrypoint
    data_controls.py  model-improvement switch (rest read-only)
    connectors|wallet|secure_store|messaging|devices|
      help_support|legal.py  read-only inventories

Dependency order (no cycles): mouse <- dialog <- controls <- tabs/* <- toggles. Tab modules own their contracts (headings, labels, slugs, modes); toggles.py only addresses them.

Toggle addresses

Static: permissions.connector_defaults, permissions.web_access (auto_allow/always_ask); permissions.advanced.transparent_proxy|tls_interception| sni_mismatch_rejection, data_controls.ai_improvement (on/off); general.theme (avatar/default/blue/purple/pink/orange/green/ beige/monochrome; avatar = "Match my avatar").

Families: permissions.websites:<host> (Allow/Ask/Deny), permissions.protocols:<slug> (on/off; network primitives outbound-ssh, smtp, imap-pop3, database, ftp, dns, other-tcp, other-udp pinned live 2026-10-06, MCP titles kept defensively; unique substrings like ssh also resolve).

Every set verifies in place and reads back through a fresh session; readback mismatch reports failure, never partial success. Switch commits can land slowly (or on dialog close), so in-flow verifies poll and the fresh readback is the source of truth; sets that only the readback confirms carry "readback_only": true. Website Ask/Deny is one-way: the override row leaves the allowed list (no add UI), verified by absence; absent hosts read as "not in Websites list" (effective: web-access default). Switch sets click once and never fall back to a second click: a re-click would undo a slow commit (switches toggle). Advanced switches take one synthetic click; protocol and Data-controls switches need trusted clicks (synthetic is a proven no-op there). Radios keep their trusted-click fallback (idempotent). data_controls.ai_improvement is read-only: the site ignores every input gesture on that switch (proven live; reads fine, sets raise). Caller errors (unknown node/toggle/tab/value) raise MenuError before any CDP traffic. Transport failures return {"ok": False}.

CLI

box chromebox permissions <node> list [--tab TAB] [--json]
box chromebox permissions <node> get <toggle> [--json]
box chromebox permissions <node> set <toggle> <value> [--json]
box chromebox permissions <node> describe <tab>   # JSON inventory

Exit 2 on caller errors, 1 on transport/unreadable toggles.

Patch guide

Site change Patch
Dock button / dialog open flow dialog.py (open_settings)
Radio/switch mechanics controls.py
Permissions headings, modes, slugs, adv labels tabs/permissions.py
Usage text, theme values tabs/general.py
Data-controls switch label tabs/data_controls.py
New settable toggle tab module contract + toggles.py registry row
Trusted-click transport mouse.py

describe <tab> dumps a tab's live inventory for debugging.

Provenance

Contracts come from live read-only DOM recon (2026-10-06): Permissions radios auto_allow/always_ask, Websites Allow/Ask/ Deny chooser (real click to open), 8 protocol switches, Advanced section (Transparent proxy off, TLS interception off, SNI mismatch rejection on), Data-controls single switch, General theme radios. Radix menus need real Input.dispatchMouseEvent press+release with monotonic CDP ids; synthetic clicks fail. Recon never touches Reset.

Tests

tests/test_hatch_menu.py (mocked CDP, per-module): mouse/trusted-clicks, dialog open-retry/tab/row flows, control verify-then-fallback, registry resolution + readback mismatch, tab contracts on live-captured fixtures. tests/test_invite.py covers the invite shims + flat usage parse.