Files
box/docs/THREAD-BOOKKEEPING.md
T

7.3 KiB

Thread Bookkeeping with Pin & Archive

Box is the main surface. All operator work goes through Box (box.muse-dev.online). The web UI, box CLI, and agents share the same API endpoints. No UI-only powers.

Status: runbook (2026-10-04) Applies to: muse.ai side-chat threads used by the fleet DM system

Why bookkeeping matters

Side-chat threads are addressed by UUID. UUIDs go stale (deletion, account-side cleanup, platform reissue — observed 2026-10-04 when heartbeat-opm changed UUIDs in a single day and when the hardcoded 1e75a740 "646 tasks" UUID died ~90 minutes after being committed). Stale UUIDs cause silent misdelivery: the SPA redirects to an arbitrary valid thread instead of 404ing, and the old placement-blind verify claimed success.

Duplicates compound the problem. On 2026-10-04 the user observed two BRAIN-INIT messages, indicating two brain threads where one was expected. Fuzzy sidebar name search then matches the wrong thread.

Pin and archive are the native muse.ai mechanisms for managing this. They are gateway API methods (/api/session/pin, /api/session/unpin, /api/session/archive, /api/session/unarchive, /api/session/delete, /api/session/rename), not CDP browser actions. Our muse-chat-api.py (CDP-based) does not implement them.

Tooling: muse-cli

The supported path is the third-party muse-cli tool (nikships/muse-cli), which speaks the gateway protocol (Noise XX + protobuf) directly. It is not installed on bl, the VM, or the operator container as of 2026-10-04.

Install (on bl)

# Node.js required
npm install -g muse-cli   # or per-repo install per muse-cli README

Auth

muse-cli needs a hatch_sess cookie from a logged-in browser session. Obtain it from the operator's own browser profile — never from another agent's session. Treat the cookie as a credential: transient use, never logged, never in chat.

Commands (per muse-cli PROTOCOL.md)

muse-cli pin <session_id>          # pin a thread
muse-cli unpin <session_id>        # unpin a thread
muse-cli session-archive <session_id>    # archive a thread
muse-cli session-unarchive <session_id>  # restore an archived thread
muse-cli session-rename <session_id> <title>  # rename a thread
muse-cli session-delete <session_id>     # delete a thread (destructive)

<session_id> is the thread UUID (the hex string in https://muse.ai/thread/<uuid>).

When to pin a thread

Pin threads that are critical infrastructure or frequently accessed during triage:

Thread Alias / reuse_key Why pinned
main-loop brain main-loop brain Fleet coordination point
646 tasks 646 tasks 646's primary work thread
heartbeat heartbeat-opm Pipeline health signal
646-pip-coord 646-pip-coord Cross-operator coordination
muse tasks muse tasks muse's primary work thread

Pinning keeps these at the top of the sidebar, which:

  • Prevents fuzzy-name-search confusion (searching "646" is less likely to match the wrong thread when the right one is pinned and visible).
  • Makes important threads easy to find during incident triage.
  • Does not change the UUID — pin is purely a display/ordering operation. job-sidechats.json mappings are unaffected.

When to archive a thread

Archive threads that are stale, duplicate, test, or dead:

  • Duplicates: the double BRAIN-INIT incident — archive the surplus thread, keep the registered one.
  • Stale UUIDs: threads whose UUID no longer resolves (e.g. the dead 1e75a740 if it still exists on 646's account).
  • Test threads: test-auto-prov (b96dd020), pipe-7d2896, pipe-1b4579, and other autoprovision experiments.
  • Superseded threads: old threads replaced by a renamed or re-provisioned canonical thread.

Prefer archive over delete: archiving is reversible (session-unarchive), deleting is not. Delete only when certain the thread holds nothing worth recovering.

Listing pinned vs archived threads

muse-cli's session listing distinguishes state. Our CDP-based sidechat list scrapes visible sidebar text only and cannot distinguish pinned/archived/deleted threads — another reason to use muse-cli for bookkeeping operations.

After any pin/archive operation, verify with the list command and confirm the expected state before updating mappings.

Integration with job-sidechats.json

job-sidechats.json (on bl) is the dynamic UUID registry: { "<alias>": { "thread_uuid": ..., "agent": ..., ... } }.

Rules:

  1. Pin changes nothing in job-sidechats.json. The UUID is the same; only sidebar ordering changes.
  2. Archive the registered thread → update the mapping. Either remove the entry (so the next send autoprovision a fresh thread) or point it at the surviving canonical thread's UUID.
  3. Archive a non-registered duplicate → no mapping change needed, but note it in the commit message for traceability.
  4. Never hardcode UUIDs in SIDCHAT_ALIASES or elsewhere (lesson from 2026-10-04). Use name-based autoprovision with the dynamic registry. A hardcoded UUID is a ticking time bomb: it will go stale, and there is no alert when it does.

Case study: duplicate brain INIT (2026-10-04)

What happened. The user observed two BRAIN-INIT messages in the UI, indicating two brain threads where the design calls for one. The brain thread (5f18476d-8994-49e7-a9e0-4838732363fe, created 19:40 UTC on opm's account with placement confirmed) was never registered in job-sidechats.json, so 646 reported it as "still pending" and "unwired" while a second INIT appeared.

Contributing factors.

  • The brain thread was created but the registration step was skipped.
  • Without registration, later code paths could not find the canonical thread and created (or appeared to create) another.
  • No pin/archive pass had ever been run, so there was no canonical "this is the one true brain" signal in the sidebar.

How archive fixes it.

  1. Identify both thread UUIDs (from dm-log alias_resolved / nav_ok records or muse-cli session list).
  2. Confirm which UUID is registered in job-sidechats.json under "main-loop brain" — that one is canonical.
  3. muse-cli session-archive <duplicate_uuid> on the surplus thread.
  4. Pin the canonical brain thread so it stays visible.
  5. Commit the job-sidechats.json state with a note referencing the archived duplicate.

Prevention. After creating any canonical thread: register it in job-sidechats.json immediately, pin it, and note the UUID in the commit message. Run a periodic (monthly) archive pass over test and stale threads.

Quick reference

# one-time: install + auth (hatch_sess cookie, transient)
# pin the critical set (opm account, adjust per agent)
muse-cli pin 5f18476d-8994-49e7-a9e0-4838732363fe   # main-loop brain

# archive a duplicate / stale thread
muse-cli session-archive <duplicate_uuid>

# verify, then update job-sidechats.json on bl if the archived
# thread was the registered one (re-point or remove the entry)

Open items (2026-10-04)

  • muse-cli is not yet installed anywhere in the fleet.
  • hatch_sess cookie handling needs a defined transient-use procedure.
  • No scheduled archive pass exists yet; propose monthly.
  • CDP-based sidechat list cannot see archived state — bookkeeping must go through muse-cli, not dm.py.