Regression from wiring the peers scope: requestArchyAllContent awaited all three scopes together, so the grid waited on the slowest. `peers` browses every federated node over FIPS (Tor fallback) and routinely takes tens of seconds or times out when a peer is offline. On-device that read as "content(peers) failed: Content request timed out" plus an AIUI that felt very slow to open — with nothing rendered meanwhile, even though local content was ready immediately. Now `own` paints as soon as it lands and `owned`/`peers` fold in as they arrive. A scope that times out costs only its own results. Also records the operator's console findings as tasks: the `files` context timeout, the web-search CSP block (13-09, now firing on every query), the strfry icon 404, IndeeHub's relay.nostr.band socket, and the ask that `!archy` over mesh be able to action container commands with text responses. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
8.4 KiB
AIUI answers in prose where the content + context surfaces should carry it
Operator-reported 2026-08-06, with a full exported transcript as evidence ("Exploring My Node", exported 8/7/2026 12:57 AM). Add to the fix → test → debug → fix loop.
The complaint
Almost every answer in that transcript is rendered as markdown prose in the chat window — bullet lists, fenced code blocks, a hand-built table — when the phase already has a content surface (cards/grids) and a context surface for exactly this. The chat window is doing work the surfaces exist to do, so the product reads as a chatbot that talks about the node rather than an interface onto it.
Every case in the transcript, and what should have rendered
| Turn | Rendered as | Should drive |
|---|---|---|
| "What apps do I have installed?" | bulleted list of 6 apps | app grid — icon, status pill, Open action per app |
| "How's my Bitcoin node doing?" | bold key/value prose | node status card — height, sync %, mempool gauge, fee band |
| "What's my Lightning balance?" | bold key/value prose | balance card + channel list (5 channels, 8 peers) |
| "What files do I have stored?" | bulleted list of 10 of 47 | file grid — type icons, folders navigable, open actions |
| "Read my todo.txt" | fenced code block | file viewer surface |
| "Show me my bitcoin.conf" | fenced code block | config viewer, redacted (see below) |
| "Mempool seems slow, check?" | 10 log lines in a fence | log viewer surface — scrollback, level filter, follow |
| "Open Mempool" | ✅ opened the app frame | (this one is right — the model of the rest) |
| "What other apps can I install?" | 3 grouped bullet lists | marketplace grid with Install actions |
| "Full status summary" | a hand-built markdown table of 14 apps | dashboard surface — system health tiles + app grid |
Only 1 of 10 turns used a surface. The content is correct in every case; the presentation channel is wrong.
Why this is more than cosmetics
- The grids/cards carry actions (open, install, restart, follow logs). Prose carries none, so every answer dead-ends and the user has to go find the app.
- It burns model tokens re-formatting structured data the node already has structured, and invites the transcription errors we already fixed once in the content-card parser (title n paired with description n−1).
- A markdown table of 14 apps is unreadable on mobile; the app grid is responsive.
The rendering contract (operator, 2026-08-06, second note)
"the chat rich content often overflows and should always be a mini version where content surface and context windows expand on that version."
So this is not "move everything out of the chat" — it is a two-tier contract, and it applies to rich content the chat already renders as well as the prose above:
- In the chat: always the mini version. Compact, bounded height, never overflows the bubble — a summary chip/card. 14 apps become one "14 apps, all healthy" tile, not a 14-row table. A config file becomes a named file chip, not 30 lines in a fence.
- The content surface and context window are the expansion target. Tapping the mini version (or the intent itself) opens the full grid / viewer / dashboard there.
- Overflow in the chat is the symptom to test against: nothing rich should be able to blow out the bubble at any viewport. Check mobile first — the 14-row markdown table is the worst current offender.
Applies to every row of the table above: pick the mini form AND its expanded surface for each intent, rather than treating them as separate designs.
Security defect found in the same transcript — fix regardless
The bitcoin.conf answer printed rpcpassword=archipelago123 in cleartext into
the chat. Whatever the source (this looks like mock data), the rule has to hold: a
config file rendered by the assistant must be redacted before it reaches the model
or the transcript — rpcpassword, rpcauth, tokens, keys, mnemonics. This is the
phase's own non-negotiable (keys out of the browser and the model).
A test was RED alongside this — it was a stale fixture, not a live hole, and it is now fixed. Recording the correction because the first read of it was wrong:
assistant::backends::routstr::tests::secret_shaped_content_never_reaches_the_stub
panicked at archipelago/src/assistant/backends/routstr.rs:1073
"a secret-shaped body must be blocked"
screen_outbound was rewritten for precision on 2026-08-06 to validate the BIP-39
checksum instead of matching a word-run shape — the shape rule had blocked every
legitimate turn on a live node, twice. egress.rs's own test was updated to a
checksum-valid mnemonic; this routstr copy still used the first twelve wordlist
entries, which is not a parseable mnemonic, so it asserted behaviour that had been
deliberately retired. A real mnemonic is still blocked on the Routstr paid leg
(screen_outbound runs at routstr.rs:516, before any body is sent), and
checksum-invalid runs of 20+ wordlist members are still caught by
IMPLAUSIBLE_MEMBER_RUN. Fixture corrected to a checksum-valid mnemonic.
Pre-existing, from 13-13 — unrelated to the appgate Authorization fix (that commit
touched only appgate/mod.rs; routstr.rs has no reference to appgate).
1300 passed / 1 failed → now green.
Suggested shape of the work
- Redact secrets in file/config rendering, node-side, before the model or the
transcript sees it.
rpcpasswordin cleartext is the proof it is not covered —screen_outboundguards the outbound cloud leg, not what gets rendered back. - For each intent in the table: choose its mini form (chat) and its expanded surface (content/context), per the rendering contract above. Both, together.
- Audit which intents already have a surface and are simply not routed to it vs.
which have no surface yet. Ties directly to the open item "audit all ten
AIContextCategoryvalues infetchAndSanitizefor real coverage, not stubs". - Make structured model output the contract for these intents rather than parsing prose back into cards — the same conclusion the content-card parser item reached.
- Test overflow at mobile viewport for every mini form; the 14-row table is the current worst case.
Loop
Per RESUME-2026-08-06-media-loop.md: fix → build → deploy to archi-dev-box → test
live → verify the shipped bundle changed → commit → push. No item is done without a
live check on the node.
Added 2026-08-07 — operator console evidence
content(peers) timed out+ AIUI slow to open — CAUSED by the first version ofrequestArchyAllContentawaiting all three scopes together, so the grid waited on the slowest.peersbrowses every federated node over FIPS (Tor fallback) and routinely takes tens of seconds. FIXED: progressive —ownpaints immediately,owned/peersfold in as they land, a timeout costs only its own scope.files fetch failed: Context request timed out: files— thefilesCONTEXT category (not content) is slow enough to hit the broker's deadline. Separate from the above; needs its own look at whatsanitizeFiles()does.- Web search blocked by CSP —
connect-src http://<node>:*/aiui/blocks/api/web-search. Already recorded as 13-09's decision (the web-search setting must drive the CSP node-side); now reproduced on every single query, so it is noisy as well as broken. strfry.png/strfry.svg404 — missing app icon.wss://relay.nostr.bandclosed before established — IndeeHub's own relay connection, from its page. Separate from the local relay fix.- Operator ask:
!archyover mesh must action commands. The mesh AI should be able to restart/stop/install containers and everything else that makes sense, with text responses — i.e. the same tool surface the embedded assistant has, reachable from a mesh message.
Assistant tool coverage (root cause of "I don't have a tool for that")
content_list mapped ONLY to content.list-mine, so asking about peer films got an honest
"I have no tool" — the model was right. Now takes scope: own | peers | purchased | films,
dispatching to content.list-mine / content.browse-all-peers / content.owned-list /
content.indeehub-projects. Peer browsing rides FIPS (PeerRequest::new(fips_npub, onion, "/content"), 6s FIPS fast-fail then Tor), which is what the operator meant by "we use FIPS
for that" — the onion is the peer's identity, FIPS is the transport.