feat(preview): check and install newer preview builds at startup - #374
feat(preview): check and install newer preview builds at startup#374jonybur wants to merge 3 commits into
Conversation
Preview installs are self-hosted (update_url feeds -> GitHub release artifacts), but peerd's offscreen keepalive holds the MV3 SW alive, which is exactly the state where Chrome parks a downloaded extension update forever. background/update-check.js closes that gap: Chrome forces the update_url poll at boot and reloads when a downloaded update can apply with nothing live (no turn or goal run, no UI port, no engine tab, no other extension page), re-attempting when a surface closes; Firefox (no requestUpdateCheck API) reads the gecko feed and offers the XPI in a notice, persisted in storage.session so it survives event-page respawns. The onUpdateAvailable listener registers ONLY on self-hosted manifests: on Firefox a listener's mere presence defers every add-on update until reload()/browser restart, so a listener on the store package would break AMO's automatic updates; a disabled toggle there restores the no-listener default (apply immediately). Feed update_links are pinned to github.com/peerd.ai and version strings shape-checked before they reach the notice. Setting: autoUpdateEnabled, a preview-only channel key (absent from store CHANNEL_DEFAULTS, patch-gated on key presence), default ON, with an Auto-update toggle in Settings -> Behavior. Dev (load-unpacked) has no update_url so every path is a structural no-op there. The side panel's NoticeBar gains a generic https-only open-url action. Signed-off-by: Jonathan Bursztyn <jobur93@gmail.com>
|
The UI renders differently than the committed baselines — 2 of 54 screens changed (before ⟶ after below). options-behavior —
|
The options-behavior captures moved 0.32% - the new Auto-update row in the Behavior band. Reseeded on the CI authority via the update_visual_baselines dispatch and eyeballed per PNG; goal-running (0.01%, a tool-card ms flap) and multi-turn-transcript.dark (0.0000% within tolerance) rode along from the fresh capture. Signed-off-by: Jonathan Bursztyn <jobur93@gmail.com>
Visual regression✅ No visual drift. Every state renders identically to the committed baselines. 📸 Browse the visual gallery — 58 baseline screens (light + dark) as of this commit. |
Composer: the input textarea never grew with its draft, and above the
370px wrap threshold the mode-row pills squeezed until their labels
broke onto two lines. The textarea now autosizes (bounded by the
existing min/max heights, re-run on panel resize) and the mode row
wraps at every width with nowrap pills. The narrow-sidebar e2e check
now asserts wrap-plus-unsqueezed across the width matrix instead of
the old nowrap-above-370 rule.
Redesign turns 1-3 remainder (a11y): nav rail aria-current, wired
aria-controls on the "Why this matters" disclosures and denylist
groups (including the expanded-but-empty body), a permanently mounted
role=status outcome banner, and armed-confirm focus management - the
destructive verb takes focus on the arm gesture only, Escape/Cancel
return focus to the arm control, and a completed action lands on
search.
Design turn 4:
- Stop card (4c): an origin-lock stop renders in the transcript as a
slotted card (STOPPED + group chip, origin-only headline, the
landing rule's verbatim reason, the what-peerd-doesn't-know box)
instead of prose paragraphs. The card model is pure in
origin-lock-report.js and rides the actor-reply envelope; the
orchestrator's prose report is byte-identical to before. Only the
handoff card carries an action - prefill the composer with the
sessionless-read message; it never sends and grants nothing. No
generic stop offers an action: the landing is the one address a
hostile page controls, and a button would turn a page-chosen
destination into a one-click trusted instruction.
- Confirm modal (4d): the session button states what it actually
grants ("Allow all writes" plus "this chat, this site/any site",
derived from the same rule as the grant key), a helper-raised
prompt explains why the session grant is absent instead of hiding
it silently, and a quiet "No answer counts as Reject." line appears
at ninety seconds - timed from the prompt's raise, re-armed per
prompt id.
- Settle outcomes (4e): the confirm coordinator now reports how each
prompt settled (answer/timeout/abort/stop, plus which surface
answered); the SW records settles no surface saw - including
fail-closed denials while peerd wasn't open - and replays them via
the snapshot; the transcript renders one quiet line per settle,
suppressed on the surface that answered. Outcomes for actor-raised
confirms map to the root chat so helper prompts are covered too.
Verified: eslint, strict tsc (coverage floor 699 -> 705), copy
hygiene, boundary/imports/pages checks, 5722 bun tests, 871
in-browser tests (new reducer-fold suite), e2e 68 states / 322
checks including two new visual states (sidepanel-confirm,
sidepanel-stop-card). The new states need the update_visual_baselines
dispatch after merge to gain committed Linux baselines.
Signed-off-by: Jonathan Bursztyn <jobur93@gmail.com>
|
The preview update work is worth landing, but this branch now conflicts with main and the third commit adds a separate UI, confirmation, and origin-lock redesign. That makes the current diff too broad to review or merge safely. I am extracting the first two commits onto current main for focused adversarial review. The UI redesign should remain separate so it can receive its own security, user UX, model UX, and visual review. |


Preview installs update via the self-hosted update_url feeds, but peerd's offscreen keepalive holds the MV3 SW alive - exactly the state where Chrome parks a downloaded extension update forever waiting for an idle that never comes, so preview testers quietly fall behind.
What changed
background/update-check.js: on Chrome, force the update_url poll at boot / panel-open (coalesced + throttled; only a completed check burns the window) andruntime.reload()when the downloaded update can apply with nothing live (no turn or goal run, no UI port, no engine tab, no other extension page), re-attempting when a surface closes. On Firefox (norequestUpdateCheckAPI), read the gecko feed and offer the XPI via a notice, persisted instorage.sessionso it survives event-page respawns.onUpdateAvailablelistener registers ONLY on self-hosted manifests: on Firefox a listener's mere presence defers every add-on update until reload/browser restart, so a store-package listener would break AMO auto-updates; a disabled toggle restores the no-listener default there.update_linkhosts pinned to github.com / peerd.ai; version strings shape-checked.autoUpdateEnabled: preview-only channel key (absent from store CHANNEL_DEFAULTS, patch-gated on key presence), default ON, toggle in Settings -> Behavior. Dev (load-unpacked) has no update_url, so every path is a structural no-op there.open-urlaction.How verified
56 new bun tests (feed parsing, version compare, throttle/coalesce races, listener gating, Firefox disabled-default, notice persistence across respawn) + the settings-patch gate tests; full local gate set on the branch: typecheck, lint, boundary, imports matrix, copy/hygiene/invariants, tscheck floor (698 -> 699), gen drift, 5712 bun tests, 865 in-browser, packaged page boot both channels, pinned-Firefox suite (858 Gecko + store smoke), live e2e 318/318 across 66 states. Live feeds confirmed reachable with open CORS.