Repository navigation
Closed
Labels
type:choreMaintenance, deps, tests, docsMaintenance, deps, tests, docs
Description
Motivation
The phone companion (src/remote-client) mounts the real desktop Feed, but it gets there through copies instead of shared code, and each copy has drifted. Fixes keep landing in only one copy, which is why the phone keeps looking broken after many fixes.
These divergences are observable on the phone:
- It renders raw
entriesinstead ofselectMergedEntries, so the ghost fallback is missing. - It skips provider
normalizeConditionsand uses a separate condition dispatch. lastJsonlEntryAtis hardcoded to0, so the collapsed-running rule never fires (ARCHITECTURE §8.3).- Main's remote tap has no semantic coalescing and no ordering barriers against JSONL, history-boundary or removal. The desktop forwarder has both.
- The phone never receives
transcript-diagnosticorprovider-session-changed. - A second sub-agent watcher polls the same directories as the first.
- History loading sits outside the
SessionFeedcontract, with two separate paths. SessionView.tsxhand-mirrors TileLeaf's Feed props, citing line numbers that are out of date.- Six Vite aliases stub renderer modules, and the app-store stub is not type-checked.
There are also three rendering-modularity debts:
- Per-provider policy is hard-coded in the shared decide layer:
SUPPRESSION_POLICY, plus inlineprovider === ...checks. - Provider rows import feed and workspace internals.
'claude'is used as a silent fallback for unknown providers.
A planned screenshot viewer, which runs live agents and captures their rendering, needs exactly the same seams.
Intended behavior
Each concern gets one implementation, with the transport or host injected:
- Main: one transport-neutral session feed tap, with window and remote sinks.
- Contract:
SessionFeed.loadHistoryon the contract. - Ingest: one pure session-ingest core used by both the desktop hub and the phone store. Desktop-only planes stay layered on top.
- Host: a typed
RendererHostcontext in place of the phone aliases and the straywindow.apicalls in the feed subtree. - View: one
AgentFeedView, mounted by both TileLeaf and the phone. - Provider policy: moves into the renderer registry.
- Imports: provider rows import feed primitives only through
features/feed/public, and the reverse import boundary is tested. - Docs: ARCHITECTURE and the rendering docs are updated to match.
Plan: docs/plans/2026-09-24-phone-shared-rendering.md
Acceptance criteria
- The phone paints from the same merged entries, normalized conditions and
lastJsonlEntryAtas the desktop. - The phone receives the same ordering barriers and the two missing channels.
- The remote-client Vite config has no component or store aliases, and its bundle contains no Monaco.
- A test replays one recording through both clients' ingest and gets the same ledger items.
- No behavior change on the desktop: the existing suites pass, and
npx tsc -bis clean.
Metadata
Metadata
Assignees
Labels
type:choreMaintenance, deps, tests, docsMaintenance, deps, tests, docs
Activity
Juliusolsson05 commentedon Sep 25, 2026
Implemented in #1186.
Scope change from the original plan, recorded in the plan's implementation notes: there is no
features/feed/publicbarrel. Provider rows importingMarkerRow, the feed contexts and nested rows is legitimate row composition, and a barrel would only change module evaluation order inside the existing registry ↔ rows cycle. The actual violations were provider imports of the desktop workspace, and all three are removed:TileLeafregistry is deleted;The existing import-boundary test gained the reverse rule, so
src/providers/**can't import@renderer/workspace/**again.Also found and fixed along the way:
lastJsonlEntryAtwas hardcoded to 0 on the phone;fix(sessions): keep a failing feed sink from exiting the app
fix(rendering): recompute the ledger when its policy input changes
fix(remote): clear a recovered live channel's fault on the phone
test(remote): compare phone and desktop ledger rows from recordings
chore(build): drop the deleted renderer registry from the tsconfigs
test(conditions): probe the outlet's onPtyAction, the prop #1177 renamed