Skip to content

Browser messages and thread transcripts ​

The replacement dashboard uses the current execution engine through HomeSession. Home shows assigned questions and work context; one composer sends to the explicitly selected destination. Record 0102 replaces the old rail and pinned Plane composer. Native adapter APIs remain compatible; their tests do not prove production activation of the new browser host.

  • Code: web/green/pages/ThreadBrowser.vue, web/green/pages/Thread.vue, web/green/components/ReadonlyThread.vue, web/green/components/ConversationDock.vue, web/green/components/ConversationComposer.vue, web/green/lib/homeSession.ts, web/green/lib/readonlyThread.ts, web/src/components/home/, web/src/lib/runStream.ts, src/channels/web.ts, src/channels/homeRuntime.ts, src/channels/homeSeed.ts, src/channels/webSeed.ts.
  • Tests: web/green/pages/discovery.test.ts, web/green/pages/reportStates.test.ts, web/src/pages/home.test.ts, web/green/frontend.test.ts, web/green/pages/homeActions.test.ts, web/green/components/conversationDock.test.ts, web/green/lib/homeSession.test.ts, web/src/lib/runStream.test.ts, src/channels/web.test.ts, src/channels/homeRuntime.test.ts, src/core/authz/channelDirectory.test.ts, src/core/authz/actor.test.ts, src/core/authz/grants.test.ts.
  • Docs: browser-home.md, live-view.md, authorization.md, thread-admission.md, record 0102.

Behavior ​

  1. Home uses one private broad session and one explicit-destination composer. Reading a thread does not select it for a reply. A fresh server proof is required before Reply changes the destination.
  2. Recorded run turns retain their request, receipt, answer, facts, and shared replay fold. A failed run without a reply shows its recorded outcome. Silent receipts and the parent's recorded words stay in transcript order; neither creates a model run.
  3. Current source or identity loss clears private transcript, drafts, and pending retries. View as remains read-only. Unknown submission outcomes retain the issued key and immutable payload; a retry checks that saved submission.
  4. The removed conversation rail, local owner toggles, brand motion, and pinned Plane chat are superseded. Their backend run, thread, and saved-choice APIs remain available to native clients.

Validation criteria ​

CriterionEvidence
Recent thread discovery retains history-store and ledger unavailability and both run/thread bounds. A healthy empty recent window, unavailable read and partial loaded list remain distinct. Every list is current-access filtered; no seed claims a full retained-history census.[unit] src/channels/web.test.ts::GET /threads and /threads/<id> — the seed from the runs service (items 2, 7, 8, 11)::preserves unavailable and partial thread discovery separately from an empty authorized recent window, ::marks the bounded thread rail partial when additional authorized conversations are omitted, web/green/pages/discovery.test.ts::Thread discovery availability::distinguishes healthy empty, unavailable and partial authorized thread windows, web/green/pages/reportStates.test.ts::Green report states::limits thread discovery and filtering to the loaded browser list without implying full history coverage
Recorded run turns retain request, receipt, answer, outcome, and one shared replay read. User stops use info colors; failed and interrupted runs keep warning colors.[unit] web/src/pages/home.test.ts::HomePage — a finished conversation (items 4–6)::*
A silent receipt stays between the two recorded run turns[unit] web/src/pages/home.test.ts::HomePage — silent receipts on the thread view (item 12)::a silent receipt draws as a read-not-answered turn with its reason, in order among the runs
The parent's recorded word stays in order and retains its original run link and live token[unit] web/src/pages/home.test.ts::HomePage — the parent's word on a unit's thread (item 2)::*
One composer changes its announced destination only after explicit authorized focus; current revocation removes transcript and editor[unit] web/green/frontend.test.ts::Green browser frontend::mounts one composer and changes its announced destination only after explicit thread focus, ::removes the composer and transcript on an active private-feed revocation
Unknown outcomes keep the original issued key and exact body[unit] web/green/lib/homeSession.test.ts::Home conversation destination::retries an uncertain response with the original issued key and exact payload

Retained native browser adapter compatibility ​

These rows test the retained adapter protocol independently of the active dashboard entry. The new browser admission and identity authority are specified in browser-home.md and organization.md.

CriterionEvidence
7: the rail's seed names a thread's channel when the name directory knows it — channelName beside channelId on a row from another surface, nothing on the viewer's own lane, the id alone when the lookup has no answer or fails[unit] src/channels/web.test.ts::GET /threads and /threads/<id> — the seed from the runs service (items 2, 7, 8, 11)::the rail names a thread's channel when the name directory knows it (record 0042, the dashboard reads names): channelName beside channelId on the Slack row, nothing on the viewer's own lane, the id alone when unknown or failing
2: a conversation whose runs carry an instance tag lists the parent's ship_unit events naming this thread in time order among its runs and none naming another thread; a conversation with no tagged run reads no parent, and so does a viewer whose predicate excludes the parent[unit] src/channels/web.test.ts::GET /threads/<id> — the parent's word on a unit's thread (item 2)::*
2, 12: two silent receipts seed two read-not-answered turns with their reasons, interleaved among the runs by decidedAt, an addressed receipt seeds nothing; zero receipts, a null ledger and a failing one seed the runs alone; a thread the viewer may see nothing of stays the 404 and the ledger is never asked; a receipt turn is skipped by the history a run reads[unit] src/channels/web.test.ts::GET /threads/<id> — silent intake receipts on the thread view (item 12)::*, src/channels/web.test.ts::the projections — requester, turn, history, threads, title, palette, chips::historyOf: a receipt turn is no one's line — the history a run reads carries the runs alone
11: the routes — /threads, /threads/<id> (a conversation id or a whole thread key, encoded or not), /threads/<id>/send; anything else under the prefix is not a route; a plain id is the session's own lane, a key with a colon another channel's thread[unit] src/channels/web.test.ts::parseThreadsRoute — /threads, /threads/<id>, /threads/<id>/send::*
11: a plain request at /threads/<id> dispatches { userId: access:<sub>, channelId: web:<sub>, threadKey: web:<sub>:<id>, text, receivedAt } and, once the run exists, answers 202 with the run's own view path; a linked session sends as its person with the session as authenticatedAs; a run-less reply is 200 with the text; a run discarded before its token was read is answered as run-less; a run reads the thread's finished turns as its history only when it asks[unit] src/channels/web.test.ts::POST /threads/<id>/send — the body into dispatch() as this session (item 11)::a plain request dispatches…, ::a linked session sends as its person…, ::a request the pipeline answers without a run…, ::a run gone from the registry before its token was read…, ::a run reads the thread's finished turns as its history…
11: refusals before dispatch — GET 405, a foreign origin 403, a non-JSON body 415, an empty text 400, another channel's thread 403, an oversized body 413 — and dispatch is never reached[unit] src/channels/web.test.ts::POST /threads/<id>/send — the body into dispatch() as this session (item 11)::refusals before dispatch…
11 (record 0060): the send's handle can open a thread of its own — openThread mints a conversation in the session's lane, two calls two ids, the child's handle bound to the new key — and resumeWebIO rebuilds a handle from a bare thread key: history() lists the thread's runs as the session's own actor, an out-of-run reply resolves and logs the length never the text, undeliverable set, and a key that is not a web conversation rebuilds nothing[unit] src/channels/web.test.ts::the web handle rebuilt from a bare thread key — threadIoFor's web: arm (record 0060; web-chat item 11)::*, src/channels/web.test.ts::POST /threads/<id>/send — the body into dispatch() as this session (item 11)::the dispatched handle can open a thread of its own…
2, 11: /threads mints a conversation of the viewer's own lane with no turns, a send URL under it, the palette the viewer may run and chips grounded in their runs, and carries no token; /threads/<id> seeds exactly the thread's runs the viewer may read, oldest first, each with its request, reply and route, a live run with its token, and the rail lists the viewer's threads across channels, newest first, each with its title and its excerpt, a foreign thread by its whole key with its surface[unit] src/channels/web.test.ts::GET /threads and /threads/<id> — the seed from the runs service (items 2, 7, 8, 11)::/threads mints a conversation…, ::/threads/<id> seeds exactly the thread's runs…
11: a stranger's thread, or one the viewer may see nothing of, is the same 404 an unknown run gives; the viewer's own empty conversation opens empty; a thread from another channel the viewer may read opens with elsewhere and refuses a send[unit] src/channels/web.test.ts::GET /threads and /threads/<id> — the seed from the runs service (items 2, 7, 8, 11)::a stranger's thread…, ::a thread from another channel the viewer may read opens read-only…
2, 8, 11: the pure projections — the requester, the turn, the history, the thread groups, the title cut, the palette by exposure and grant, the chips by the viewer's facts, the IO's on-demand history[unit] src/channels/web.test.ts::the projections — requester, turn, history, threads, title, palette, chips::*
Trusted owner aliases retain their readable threads without lending private source authority; legacy calls keep their original list[unit] src/channels/web.test.ts::GET /threads and /threads/<id> — the seed from the runs service (items 2, 7, 8, 11)::uses current proved owner scope in the loaded thread list without lending private source read authority