Repository navigation
Live preview and reload contract #13
Description
Native CI reload/capture investigation, 2026-10-02
Two failures in CI at 01aa441 have separate boundaries:
- Attempt 1 completes both Firefox production and development cancellation cases, then times out navigating the extension panel after native background reload. Both base
f03f4ccand current01aa441pass unchanged and identically instrumented Firefox 157.0 development commands locally. Current navigation starts 441 ms after the edit and takes 60 ms; base starts 638 ms after and takes 64 ms. Each observes one surviving window and expected extension URL. Local macOS/Node 24 sequential runs do not reproduce concurrent Linux/Node 26 CI; no cause or pre-existing classification is claimed. - Attempt 2 stops earlier taking the final Chromium natural-idle screenshot, after its functional assertions. This also occurred at
bc5baa5; intervening unchanged full CI passed. The capture target is the first of two fresh tabs, loaded and connected but hidden. Pinned Chromium source shows hidden capture uses the native painting/surface path. Both unchanged and foreground-only local comparisons pass, so the Linux timeout's exact cause remains unproven.
Commit 0da5f55 changes only the final screenshot boundary, after every idle/recovery assertion:
await connection.command('Page.bringToFront', {}, panel.sessionId);
await connection.command('Page.captureScreenshot', {}, panel.sessionId);This removes an unnecessary hidden-capture dependency. There is no SDK change, retry, readiness controller, timeout extension or Firefox patch. The investigation records pinned sources, comparison outcomes and the attribution limit.
Definition of Ready
- Exact failing boundaries and CI attempts are separated from feature assertions.
- Actual native visibility and base/current Firefox inputs are inspected.
- A one-call native capture adjustment has a concrete scope and adds no recovery mechanism.
Definition of Done for this slice
- Maintained Chrome153 idle command passes: natural idle 30,010 ms, fresh provider/worker, restored10, one action saved11, independent key44, no worker attachment/page errors and a visible connected screenshot.
- Owned browser exits, profile is removed and the existing extension artifact hash stays unchanged.
- Affected strict Oxlint, TypeScript 7 and formatting pass.
- Combined full CI passed at
0da5f55; this does not retrospectively identify the earlier capture cause.
Run VITE_COUNTER_STORAGE_KEY=example.persisted-counter pnpm --filter @devkit/example-webext test:persistence:chromium:idle. The original Firefox navigation failure remains an acceptance investigation, with no recovery behavior added. Broad change-matrix/lifecycle obligations are not closed by this slice.
Full CI at 0da5f55 passed every workspace and native production/development gate. All 26 retained native receipts were downloaded and independently checked against every exact referenced scenario: 176 native checks, 814 API paths, 407 linked paths and 34 gap groups. This establishes acceptance of the declaration/capture slices; it does not identify the earlier intermittent failures.
Firefox panel replacement observation, 2026-10-02
The diagnosis and correction, committed as ce546ce, addresses the failure in CI 36894953619. Firefox destroyed the old Marionette actor during a native DevTools panel HTML reload while the test was reading it. The read-only readiness poll now tolerates that exact document-replacement error within its existing deadline. Unrelated failures still propagate and effectful scripts are never retried.
Five executor-boundary regression/control cases, all 14 WebExtension example units and affected strict checks pass. Both actual concurrent development suites pass, with 15 Chromium and 11 Firefox scenario groups. The unchanged local baseline passed; the native race was recorded in CI and replayed deterministically at the driver boundary. No application reload policy changed. Full CI passed at ce546ce68e7df120a1adec6141768a8ca44a24d0, including the full workspace checks, production browser suites, concurrent development transitions and executed API-evidence gate.
Firefox sidebar control readiness, 2026-10-01
Correction 8b8af184412f00522d1e545c17801337a88aae2b fixes the test's missing readiness check when opening Firefox's native extension sidebar.
CI 36877758024 passed workspace validation and all production browser suites, then failed in the concurrent development step at toggleSidebar: its immediate browser-chrome lookup returned null and the test called .click() on that value. The lookup runs before the extension sidebar document mounts. Five fresh-profile local runs of the unchanged helper passed; the failure has not been reproduced locally or dismissed as a pre-existing runtime bug. Transient native rendering and a discarded lookup during a browser-context transition remain possible causes of that null.
The helper now uses Selenium's WebElementCondition to wait up to the existing 10-second UI allowance for the actual browser-owned shadow button. It then performs exactly one native WebDriver click. Optional lookup handles an absent sidebar root, shadow root or extension button; a persistently missing registration still fails with a named timeout. No application retry, synthetic click, extension policy or SDK API changed.
Affected strict Oxlint, TypeScript and formatting pass. Actual Firefox production passes all 51 checks, and the exact concurrent development command passes 11 Firefox and 15 Chromium scenarios. Full CI passed at 8b8af184412f00522d1e545c17801337a88aae2b, including workspace build/types/strict lint/tests, all production browser suites, both concurrent development suites and the combined executed API-evidence gate.
This corrects the browser test's precondition. It adds no worker-recovery or persistence guarantee and does not reopen the settled native reload contract.
Registration HTML and Firefox observation correction, 2026-10-01
Implementation bd0354e adds direct entrypoints/devtools.html edits to both native development suites and fixes the Firefox observation race exposed by the preceding registration-module test.
The earlier CI failure was reproduced locally. The test first observed counter 9 and the expected provider in a connected options document. A separate provider read returned null; the following snapshot had a different performance.timeOrigin and was still connecting. The installed Firefox 157 Marionette implementation returns null when a non-replayable executeScript command is interrupted by document replacement. The test had combined observations from different document lifetimes. Diagnosis and sequence diagram, captured failure with successful controls.
The correction waits for connected status, provider identity and counter in one native snapshot. The existing timeout remains unchanged; it does not repeat the action or add application recovery. CI now retains development JSON and screenshots after failures.
| Direct registration HTML edit | Chromium 153.0.8010.12 | Firefox 157.0 |
|---|---|---|
| Native update | Fresh hidden registration document contains the changed HTML; existing tab remains | Native build output contains the changed HTML; extension tab disappears |
| Explicit toolbox reopen | Exactly one updated tab | Exactly one updated tab |
| Panel and action | Fresh document/caller, same provider and counter, one action effect observed by options | Fresh document/caller, same provider and counter, one action effect observed by options |
Chromium HTML receipt, Firefox HTML receipt, Firefox peer observations.
Validation: concurrent maintained pnpm --filter @devkit/example-webext test:dev passed 15 Chromium groups and 11 Firefox groups. Changed-file strict lint, affected-workspace TypeScript 7 and formatting passed. Full CI passed for this exact commit, including workspace build/types/lint/tests, both native browser suites, the concurrent development transitions and the executed-browser evidence gate. The preceding failed run remains recorded below.
This completes the direct registration-HTML check, not the full reload contract. Content/page updates, rapid edits, popup/DevTools/sidebar background/config transitions, mixed-backend view HMR and watched-production coverage remain open. Native toolbox reopening is explicit; no SDK lifecycle API or production behavior changed.
Direct DevTools registration module changes, 2026-10-01
Implementation 4681c9c adds a real registration-module edit to both maintained native development suites. The fixture changes src/devtools.ts from the panel name Devkit to Devkit updated. Production SDK and extension behavior are unchanged.
| Native observation | Chromium 153.0.8010.12 | Firefox 157.0 |
|---|---|---|
| After the imported registration module changes | Hidden registration document reloads; existing tab keeps its old name | Extension tab disappears from the toolbox |
| After explicit native toolbox close/reopen | Exactly one Devkit updated tab |
Exactly one Devkit updated tab |
| Panel identity and state | New document/caller; same provider and counter | New document/caller; same provider and counter |
| One renderer action | One increment observed by the options peer | One increment observed by the options peer |
flowchart LR
Edit[Edit disposable registration module] --> Native[Native Vite/WXT update]
Native --> Chrome[Chromium retains old tab title]
Native --> Firefox[Firefox removes extension tab]
Chrome --> Reopen[Explicit native toolbox reopen]
Firefox --> Reopen
Reopen --> Panel[One updated tab and fresh panel caller]
Panel --> State[Retained background state and one action effect]
Chromium registration receipt records the registration document timestamps and tab titles. Firefox registration receipt records the visible tab removal/reopening and panel identities. Chromium uses the existing native debugging session to observe the hidden document because normal extension view enumeration omits it. Firefox needs only its existing native toolbox controls; no new internal browser-manager inspection is retained.
The final maintained concurrent test:dev command passed 14 Chromium groups with no captured page errors and 10 Firefox groups, including native browser shutdown. Scoped strict lint, TypeScript 7, formatting and whitespace checks passed. Reproduction and native behavior table. CI for this commit failed in the new Firefox registration sequence: the options peer reached the expected counter, then a separate WebDriver provider-identity read returned null. The local concurrent run passed, and the preceding commit passed CI. The later correction above captures the same failure and proves that the separate reads crossed document replacement. CI stopped the concurrent Chromium task when Firefox failed; that interrupted run is not a pass.
No automatic toolbox reopening, recovery controller, dependency patch or new SDK API was added. This closes the registration-module acceptance gap only. Direct registration-HTML edits, remaining content/page updates, rapid edits, popup/DevTools/sidebar background/config transitions, mixed-backend view HMR and watched production remain open. Existing native behavior decisions are preserved.
Concurrent native cache correction, 2026-09-29
#13 commit b7ad4c9 fixes a reproduced concurrent development failure using Vite's native cacheDir configuration. Full CI passed, including all workspace checks, both production browser suites, and the newly concurrent development suites.
Cause and comparison
The focused probe starts Chromium and Firefox together and performs initial startup plus nine configuration restarts. Both native WXT fixtures share installed dependencies through a node_modules symlink. With the default shared physical optimizer cache, Chromium's first restart received HTTP 504 for @devframes_json-render-ui_renderer.js; its actual panel stayed at Connecting with empty provider/catalog fields. Firefox completed ten starts. This occurs before the application module initializes, so RPC retry or recovery would not fix it.
Changing only the native Vite cache directory to each fixture root made all ten starts pass in both browsers, without captured failures. Failed request and DOM, Chromium control, Firefox control.
Maintained change
vite: ({ browser }) => ({
cacheDir: resolve(import.meta.dirname, '.wxt', 'vite', browser),
}),The root separates disposable fixtures; the browser directory also separates normal Chromium and Firefox commands in one checkout. WXT already ignores .wxt for source watching. Vite owns optimization and invalidation. No new dependency patch, reload manager, retry loop or timeout increase was added.
flowchart LR
Chrome[Chromium development] --> ChromeCache[Its own native Vite cache]
Firefox[Firefox development] --> FirefoxCache[Its own native Vite cache]
ChromeCache --> ChromeUI[Renderer module loads]
FirefoxCache --> FirefoxUI[Renderer module loads]
The maintained pnpm --filter @devkit/example-webext test:dev uses pnpm regex execution to run both complete development suites concurrently. Both passed locally, along with scoped strict lint, TS7, formatting and all 3 example tests. CI now runs the same concurrent command after installing both browsers. A local sandbox initially denied the test listener; rerunning the unchanged scoped command with local-network access passed.
Diagnosis and frozen reproduction. Earlier startup/restart incidents without network captures cannot conclusively be assigned this cause. This proves the captured optimized-module failure and its correction; it does not complete the remaining reload contract. #13 stays open for the listed background/config host transitions, direct registration changes, content/page updates, rapid edits and watched production.
Actual popup HTML reload verified, 2026-09-29
#13 commit 96ace4a adds the remaining toolbar-popup HTML reload check to both native development suites. It adds tests and documentation only. The extension application and installed patches are unchanged.
flowchart LR
Edit[Edit copied panel.html] --> WXT[Native WXT HTML rebuild and page reload]
WXT --> Popup[Actual open toolbar popup / fresh document and caller]
Popup --> Provider[Same background provider and native counter state]
Popup --> Click[One JSON-rendered action]
Click --> Provider
Provider --> Peer[Options observes one counter increment]
| Assertion | Chromium | Firefox |
|---|---|---|
| Native popup remains open during HTML reload | Pass | Pass |
| Document timestamp and Port caller change | Pass | Pass |
| Provider identity and native state survive | Pass | Pass |
| One renderer button; one action effect reaches the peer | Pass | Pass, with native pointer input |
| Surface-local form resets on HTML reload | Pass | No additional claim |
| Final native close removes the popup | Pass | Pass |
The checks use action.openPopup() and native popup enumeration, edit a disposable example source copy, and wait for the actual new document content. They do not close/reopen the popup to simulate continuity, substitute a browser tab, or introduce application test hooks. The previous module HMR, pending-action, DevTools/sidebar, background and configuration assertions still run.
Chromium receipt now records 12 development groups; Firefox receipt records 8. Both complete suites pass locally, along with scoped strict Oxlint, TypeScript 7 and formatting. Full CI passed, including all workspace checks and both production/development browser suites.
One initial local run, started alongside Firefox, timed out before reaching the new popup check: the first Chromium panel stayed Connecting for 30 seconds. A separate Chromium rerun passed. Five comparisons against an isolated archive of the preceding commit 8fd14f14 all passed Chromium, so the original failure is not reproduced or classified as pre-existing. One synchronized base Firefox run instead timed out after configuration restart; its targeted rerun passed. Browser cleanup completed. Concurrent startup/restart reliability remains an open investigation; these observations do not establish a cache cause or justify another recovery mechanism. CI runs the two suites sequentially.
Popup/DevTools/sidebar background/config transitions, direct DevTools registration changes, content/page updates, rapid edits and watched-production extension behavior remain open. Cross-provider JSON action routing still needs the separate owner choice. Keep #13 open.
Native DevTools/sidebar live updates verified, 2026-09-29
#13 commit 8fd14f1 extends both maintained development suites through module HMR and HTML reload in actual DevTools panels and browser sidebars. Full CI passed, including all workspace checks and both browsers' production/development suites.
| Native interaction | Chromium and Firefox evidence |
|---|---|
| Edit panel TypeScript while host stays open | Same document/provider/state, fresh caller, one JSON action effect observed by options |
| Edit panel HTML while host stays open | New document/caller, same provider/state, one JSON action effect observed by options |
| Close native host | Native panel/sidebar disappears; the peer remains usable |
| Local form | Chromium retains it on module HMR and resets it on HTML reload |
WXT correction and its scope
The Firefox run exposed a native WXT problem. Its HTML rebuild group includes both panel.html and devtools.html. WXT reloaded both even when only panel output changed. Reloading the unchanged registration document removed Firefox's Devkit tab while leaving the toolbox open. It did not return after 30 seconds. Calling native reloadPage('panel.html') alone retained the tab and replaced its document correctly.
The exact-version pnpm patch now compares emitted HTML before and after a successful native rebuild:
const previousHtml = await snapshotHtml(groups);
await rebuild(groups);
for (const entry of htmlEntries(groups)) {
const nextHtml = await readHtml(entry);
const previous = previousHtml.get(entry);
if (previous && nextHtml && previous.equals(nextHtml)) continue;
server.reloadPage(entry.outputFilename);
}This is illustrative pseudocode. The implementation conservatively reloads when either snapshot cannot be read. It compares output, so a shared transform that actually changes devtools.html still reloads that document. Native errors and the native reload API keep their behavior. Vite remains unpatched; there is no SDK reload/recovery controller and no new public API. No WXT upstream PR was opened.
flowchart LR
Edit[Panel HTML edit] --> WXT[Native WXT rebuild group]
WXT --> Compare[Compare emitted HTML]
Compare -->|panel changed| Reload[Native panel page reload]
Compare -->|registration unchanged| Keep[Keep DevTools registration document]
Reload --> UI[Fresh panel caller / retained provider state]
Keep --> Tab[Devkit tab remains available]
The regression test uses real WXT builds and spies on its actual reloadPage boundary. It verifies panel-only change, changed registration output through a shared HTML transform, and missing previous output. The native Firefox observer also uses WebDriver's default page-load behavior, which fixes sidebar clicks that previously opened the sidebar and then failed inside Firefox's navigation observer.
Validation and remaining gates
- Scoped strict Oxlint, TypeScript 7 and formatting pass.
- All 3 example tests pass, including the native reload regression.
- Chromium: all 39 production scenarios and 11 development scenario groups pass, with zero recorded page errors.
- Firefox: all 32 production checks and 7 development scenario groups pass. No global page-error capture is claimed.
- Diagnosis, source references and raw experiment receipts.
- Chromium development receipt, Firefox development receipt, and patch removal gates.
Direct edits to the DevTools registration document retain the native lifetime and can still require reopening the toolbox. Popup-specific HTML reload, popup/DevTools/sidebar background/config transitions, content/page updates, rapid edits and watched-production extension behavior remain open. The separate JSON-renderer routing choice still needs the owner's answer. Keep #13 open.
Actual toolbar popup HMR verified, 2026-09-29
#13 commit 2190738 extends the maintained native development suites to actual toolbar popups in Chromium and Firefox. The current application already handles the update through Vite/WXT. This slice adds tests, documentation and recorded receipts; no production implementation, SDK lifecycle API or dependency patch changes.
flowchart LR
Source[Edit copied panel.ts] --> Vite[Native Vite / WXT update]
Vite --> Popup[Same actual popup document]
Popup --> Dispose[Dispose old Port / renderer / listeners]
Dispose --> Mount[New caller / one renderer]
Mount --> Provider[Same live background provider and native state]
Provider --> Peer[Options page observes one action effect]
| Interaction | Chromium | Firefox |
|---|---|---|
| Open native toolbar popup, then edit panel module | Actual popup remains open | Actual popup remains open |
| Compare document, provider and caller | Same document/provider, fresh caller | Same document/provider, fresh caller |
| Click current JSON-rendered button | One button; counter 1→2 in popup and peer | One button; native pointer click moves counter 4→5 in popup and peer |
| Close popup | Native view registry becomes empty | Native view registry becomes empty; peer remains connected |
| Surface-local input | Form survives module update | No additional form-retention claim |
| Old pending work | Completes once; replacement result is unchanged | Covered separately by the existing native experiments, not this popup check |
The tests identify the popup through action.openPopup() and extension.getViews({ type: 'popup' }). They mutate only a disposable source copy, wait for the actual updated module in both views, and inspect the real DOM. Firefox's pointer input uses measured popup geometry and asserts hover on the popup button, avoiding a click on the options document underneath. No application test hooks or substitute ordinary-tab popup are used.
The existing module, HTML, background, configuration-restart and native-shutdown assertions continue to run. Scoped strict lint, TypeScript 7 and formatting pass. Both complete native development suites pass locally. Chromium receipt, Firefox receipt. Full CI passed, including every workspace gate and both browsers' production/development suites.
This verifies module HMR for actual popups. Popup-specific HTML/background/config transitions, DevTools/sidebar development transitions, content/page updates, rapid changes and watched-production extension behavior remain open. The separate JSON action-routing choice remains pending. Keep #13 open.
Watched configuration restart verified, 2026-09-29
51b0126 moves the shared application manifest factory into the actual watched wxt.config.ts. A native watcher probe showed edits to the imported manifest.ts stayed at version 0.0.1 until the main config changed. Production imports the same factory; this fixes the gap without another watcher or dependency patch.
Maintained tests now edit 0.0.1 to 0.0.2 and prove old-browser exit, replacement manifest/provider, reset ephemeral state, working native JSON actions, and final native shutdown in both Chromium and Firefox. Chromium also checks Developer mode survives restart. The test observes the platform's native preference store and never writes protected values.
Chromium receipt, Firefox receipt, and full CI passed. Scoped lint, strict TS7, both production builds and both bundle tests passed locally. CI also ran all workspace gates and both production/development browser suites.
This satisfies the maintained config-restart proof, not the entire reload contract. Content/page updates, rapid changes, popup/DevTools/sidebar development lifetimes and watched-production extension behavior remain open.
Maintained native development, 2026-09-29
01a477f adds runnable dev and dev:firefox commands to the maintained extension example. WXT owns Vite HMR, the browser and native extension reloads. Production still uses its existing Vite build, sharing the application manifest, HTML and implementations with development. Commands and state boundaries.
The exact WXT/browser declaration and runner-ownership corrections are now root pnpm patches, with removal gates. This supersedes earlier candidate-only installation status below. Strict TS7 checks still use skipLibCheck: false; no SDK watcher, custom browser runner or state-recovery engine was added.
Live maintained tests passed on Chromium 153.0.8010.12 and Firefox 156.0.1. They verify actual native JSON rendering, retained document/provider/state during panel module replacement, full HTML reload with retained provider state, fresh provider/ephemeral state after background reload, and native browser shutdown. Chromium additionally proves an old pending command cannot overwrite replacement UI and records zero page errors. Firefox makes no global page-error claim. Tests edit temporary example copies and do not change the working tree or the normal development profile.
Chromium requires normal one-time Developer mode setup in its dedicated persistent profile. Background reload closes extension pages; reopen the options page for a fresh connection. Normal development uses native browser settings. Linux CI alone passes --no-sandbox to its disposable automated Chromium, matching the existing Playwright test default after Chrome stderr confirmed that the runner had no usable sandbox. The two live development tests now run in full CI, passed on e44cdbc, alongside the existing production suites. Local scoped build, strict lint/types/format and both bundle tests passed.
203e35d excludes the development-only WXT patches from the isolated packed-runtime consumer. 0ed2188 reports native launcher stderr, and e44cdbc applies the confirmed Linux CI launch correction. Full CI includes both existing production suites and both new native development suites.
This remains a partial completion of the broad contract. Content/page updates, repeated rapid changes, toolbar/DevTools/side-panel lifetimes and watched-production extension behavior still need implementation and live coverage. The newer checkpoint above promotes config-restart coverage into maintained tests. The renderer action-routing choice belongs to Renderer and surface contract and is unchanged.
Part of Design a portable contribution SDK and WebExtension runtime.
Extension follow-up, 2026-09-29
ce4cab5 implements native Vite panel disposal for the maintained extension source. A fresh standalone WXT fixture proves actual Chromium JSON-renderer HMR, retained worker state, both old Ports closing, exactly one routed-button listener and successful post-update actions. Full CI passed, including 24 Chromium and 18 Firefox production scenario groups. Reproduction and limits.
The narrow WXT runner-ownership patch also passes the native Firefox config-change path in 937d58c, including actual new manifest adoption, a new background generation and complete browser cleanup. Its dependency corrections remain experimental pnpm patches in standalone fixtures. WXT is not installed in the maintained workspace.
406f996 adds passing Firefox renderer HMR and Chromium HTML/background transitions. The reproduced background failure came from Developer mode being off in the native fresh profile. Enabling the normal setting makes native reload pass: old contexts close and a fresh panel uses a new provider incarnation with reset ephemeral state, without action replay. Full CI passed. A native dedicated-profile check now preserves normal Developer mode setup across browser restart and a subsequent background update after Chrome saves the setting. No protected preference hashes or policy controls are changed. Maintained development-command integration, Firefox background changes, content/page updates, all surface lifetimes and watched-production completion remain open. These slices do not satisfy the complete DoD below.
Current implementation, 2026-09-24
ae2b147 provides maintained independent build:watch/preview scripts and the concurrent pnpm-regex dev:production command, attached to both real native backends. Completed generations remain available after syntax, generateBundle and writeBundle failures, with HTTP build status and terminal diagnostics. The native provider and its state survive asset rebuilds. Follow-up 20248b4 fixes publication ownership before retained-status reads. The maintained example has 24 passing tests and passes strict package build/type/lint/format checks. Full project CI passes on that final head.
See the implementation diagram and remaining gates and runnable commands/API-to-test matrix. Browser automatic refresh/HMR, JSON-rendered diagnostics, remote SDK dispatch, extension reload and backend-state restoration remain open. A source watcher and an HTTP status endpoint do not complete those cells.
Vite draft 23574 addresses an exceptional restart cleanup defect separately. It remains a draft for the owner to take over. The workspace still uses unpatched Vite 8.3.0; normal build/reload ownership remains with Vite and native hosts.
Upstream integration boundary, 2026-09-27
Vite owns dev HMR, build watching and server restarts. Existing adapters bind their registrations to native host lifetimes. The maintained completed-build publication/status helper fills the accepted last-successful-assets requirement only; it is not a generic checkpoint/rollback/restart engine. Vite remains unpatched, and draft 23574 is an independent bug fix.
See the implementation and map review. This narrows implementation mechanisms without dropping the accepted feature or real-host coverage requirements.
Question
What is the exact live-preview and reload contract for contribution authors and host implementers across Vite development and watched production builds? Specify what updates, restarts, or reloads, which state survives, and how clients recover when a generation fails.
Context and current behavior
The requested production workflow is a watched build served through a live preview backend. It is not a requirement to download arbitrary new executable extension code into store installations. Production artifacts must also work without development-only servers or permissions when the live workflow is not enabled.
Current code supplies pieces, but not the complete workflow. In the inspected devframe checkout, packages/vite/src/hub.ts handles development lifecycle and static build output; packages/hub/src/node/initiate.ts serves compiled renderer assets; packages/hub/src/client/renderers.ts caches renderer imports and registrations. In devtools, packages/core/src/node/build-static.ts produces static output, while standalone hosting provides live RPC over compiled assets. Reading a rebuilt file with no-store does not replace an already imported browser module or dispose its old subscriptions.
Inspected revisions: devframe a3f967755af99a0a0d8dd82b7b5d4f3dd4aec0bc; devtools 121656dfc5866b2f8de5baa8e0b6166ec28599c8. Vite HMR and plugin lifecycle provide the source of truth for Vite hooks. Published-package parity is supplied by the upstream audit.
Requirements and scope
- Support both development and build-watch/live-preview as documented runnable examples.
- Apply the agreed layer-specific policy: update data/UI where supported; restart a backend/worker or reload a page when required; reconnect and restore explicitly retained state.
- A preview context must expose the raw primitives it actually owns rather than fabricate a
ViteDevServer. - Every SDK-owned lifecycle hook must have observable ordering, cleanup semantics, a working example, and automated assertions.
- Preserve provider and target identity rules from their contracts. Navigation and extension reload may invalidate document-specific handles even when retained settings survive.
- Define production build behavior independently from the optional development orchestration process.
Options and recommendation
Compare immediate per-file reload, debounced coherent build generations, and replacing the whole running session on every change. Recommend explicit successful build generations with update classification and ownership-based disposal. Determine whether retaining the last working generation is feasible for each affected layer before committing to it.
A complete in-memory state-preserving replacement of arbitrary code is not required. The contract must state each preservation boundary so contribution authors can choose state lifetime deliberately.
Proposed API or experiment
The maintained example listens to native Vite build-watch events. This sketch shows only its accepted completed-assets publication policy; it is not a backend replacement controller:
const watcher = await build({ build: { watch: {} } });
if (!('on' in watcher)) throw new Error('Expected Vite build watcher');
watcher.on('event', (event) => {
if (event.code === 'ERROR') {
publication.fail(event.error.message);
return;
}
if (event.code === 'BUNDLE_END') publication.complete();
});
// Actual host shutdown awaits watcher.close().publication is the existing example-local completed-build helper, not a new SDK export; see examples/vite-hosts/src/production-watch.ts for error handling and owned cleanup. Native backend lifecycle is separate from asset publication. Prove behavior if a provider disappears mid-update, a popup opens during transition, or the inspected document navigates before restoration. Raw local contexts must be rebound to the active generation rather than kept as stale handles.
Scenarios and acceptance criteria
Produce a matrix for changes to JSON view/state, renderer code/styles, shared contribution logic, server adapter code, background code, isolated content scripts, page-world modules, and manifests/permissions. For each development/preview host cell, specify update mechanism, affected surfaces, disposal sequence, retained state, and unavoidable user-visible reload.
Automate at least these examples:
- A JSON view update appears without remounting unrelated views; its previous event subscription is not duplicated.
- A renderer replacement mounts the new code and disposes the old renderer exactly once.
- A backend restart reconnects the UI and restores declared retained settings; ephemeral state follows its specified policy.
- A worker suspension or extension reload clears stale ports and document handles before accepting new actions.
- A failed build, failed activation, and rapid successive builds produce explicit diagnostics and a deterministically identified active generation.
- A production package with live orchestration disabled has no accidental dependency on development URLs or a watcher.
Dependencies
- Toolchain and reload audit supplies tested build/watch mechanisms and limitations.
- Server adapter contract defines live backend attachment and actual preview-context primitives.
- State scope and recovery supplies checkpoint, invalidation, and persistence semantics.
Coordinate the resulting hook identifiers and tests with Examples and API coverage contract. The final portability proof consumes this contract.
Definition of Ready
-
Identify the actual Vite/native hook and missing observable behavior for each additional reload mechanism.
-
Blocking tickets have supplied the selected mechanisms and state/lifecycle contracts.
-
Each changed runtime part has an identified owner and update notification path.
-
Development and watched-preview fixtures can be started with documented package-scoped commands.
-
Failure injections and observable generation markers are specified without adding test-only production API parameters. The maintained watcher/preview slice covers real syntax and output-hook failures; renderer/extension-specific injections remain part of their future implementation.
Definition of Done
-
Mounted browser/renderer and extension reload tests prove the documented boundaries without a second watcher or whole-host recovery controller.
-
The change/host/mode matrix specifies update, reload, disposal, and retention for every supported cell.
-
Preview attachment and generation notification protocols are documented with sequence/pseudocode.
-
Hook ordering, activation failure, stale generation, and reconnect semantics are decided.
-
Example and automated-test requirements cover every public reload hook and relevant failure mode.
-
The user has confirmed the expected authoring and reload experience.
-
Any unproved platform-dependent behavior is a blocking experiment rather than a promised feature.
Resolution record
Record the agreed lifecycle and change matrix, supporting experiments, rejected alternatives, and example/test mappings in the resolution comment. Link the answer from the map after closing the issue.
Activity
dvcolomban commentedon Sep 22, 2026
Upstream reuse audit resolution found that live hub serving reads renderer files per request with
no-store, while the client registry caches imported modules. New bytes alone do not replace an already mounted renderer. The renderer package's watch script runstsdown; its full build also runs CSS generation and Vite builds.Acceptance: observe a renderer edit through watched production build, changed served bytes and actual mounted-view update. Record module invalidation/remount behavior and restoration separately from development HMR. Specify reload/recovery for UI, renderer, content, worker and server changes. Static snapshots remain a separate mode. Serving code, registry, published build scripts.
dvcolomban commentedon Sep 22, 2026
Research handoff and acceptance gates
Sources: Toolchain and reload audit and Browser capability audit.
Accountable owner:
dvcol, until reassigned. These observations inform this open contract; they do not resolve it.Measured WXT page updates retained document time origin in both stable browsers. Custom Vite retained it in Chromium and replaced the document in Firefox. WXT Chromium content edits caused page replacement; later successful action does not prove duplicate-listener cleanup. A new Firefox background generation was observed during the fallback sequence, but the precise triggering edit remains unresolved. External-runner timeouts are not proof of WXT defects.
Two raw Firefox results are explicitly INVALID: explicit reload reused an earlier observation, and manifest adoption matched older history. Manifest adoption and complete reconnection remain unverified. The Vite watch/preview probe required explicit page reload; it supplies no automatic compiled-output notification protocol.
Acceptance protocol:
Use unique run/candidate/browser/generation identities, settled restorations, an append-only cursor per mutation, atomic command IDs and a bounded duplicate-response window. Observe backend activation and content reconnection independently. Decide retained state per affected layer, then implement explicit watched-preview success/failure notifications and last-good-output behavior. Packaged production code updates remain distinct from development HMR.
feat(examples): exercise native Vite host lifecycles (#14)
docs(reload): prove failed restart cleanup gap (#13)
dvcolomban commentedon Sep 24, 2026
Failed-restart candidate cleanup reproduced
Source commit: 8a93ac4. Exact programs, outputs and proposed patch.
Actual Vite 8.3.0 with each native host prepares a replacement before waiting for the old server's restart cleanup. When that cleanup rejects, the replacement never listens but its watcher remains active. A real file write is observed after rejection; provider readiness remains pending. This is an active-resource leak, not proof of two activated providers.
The disposable exact-version patch changes the ownership failure path:
Before/after probes pass for both Devframe and DevTools. Before manual cleanup, patched candidates have a closed watcher, zero watched directories and rejected readiness. A separate double-failure probe preserves both errors. Programs, result files, package source hashes and evidence SHA256s are committed. Script syntax and evidence hashes were checked again before committing.
The patch was applied only to a disposable package copy. The workspace Vite dependency is unchanged. It introduces no supervisor, automatic restart or operation replay. Public isolated plugin hooks cannot close a candidate they do not receive or receive notification about.
Remaining gates
Issue remains open. No upstream PR has been opened.
docs(reload): distinguish lifecycle cleanup from HMR (#13)
dvcolomban commentedon Sep 24, 2026
bee17f7 adds diagrams and clarifies scope after the owner's question about patching Vite.
The patch is not a prerequisite for ordinary HMR or build watching. Vite owns watchers, transformations, HMR and config restarts. The example's private ProviderLifetime waits for HTTP listening before contribution activation and awaits contribution disposal in closeServer. This enforces the previously accepted cleanup-before-replacement rule; it implements no watcher or restart controller.
A disposer failure rejects the close hook, exposing Vite's retained candidate watcher. The proposed patch closes the candidate and preserves failure. It neither recovers the old backend nor retries. Keeping stock Vite does not block ordinary development/preview work; the exceptional cleanup-conformance gap remains open until addressed. Patch adoption is still unapproved and no workspace Vite patch is installed.
Maintained production build:watch/preview/dev:production scripts, watched-output retention and browser refresh remain unimplemented. The separate preview probe establishes backend attachment only. Issue remains open.
docs(reload): prove native restart errors stay handled (#13)
dvcolomban commentedon Sep 24, 2026
The owner asked whether a thrown cleanup error would terminate Node and release the watcher, or whether watcher error handling could catch it. f1a2ee4 adds an executed native watched-config reproduction for both actual hosts.
The probe writes the real config file. It does not call server.restart directly, catch restart in application code, or install process exception handlers. Vite catches and logs the cleanup failure itself. The process continues, and the prepared replacement watcher observes a subsequent client-file edit. Filesystem watcher error listeners receive zero events. Explicit final cleanup closes the watcher and rejects readiness; the probe exits 0.
This distinction is trigger-specific: an uncaught programmatic await or another CLI path can have different handling. Actual process termination would release local watchers. A generic SDK cannot assume it owns the embedding process or silently terminate it. Watcher errors report filesystem-watch failures, while this watcher is healthy and the failure came from contribution cleanup.
Exact source/output files and SHA256s are retained in docs/probes/vite-failed-restart. Syntax, evidence hashes and scoped core lint/types pass. The proposed Vite patch remains unapplied. Normal Vite/preview implementation can continue with this exceptional cleanup case open.
39 remaining items
dvcolomban commentedon Sep 29, 2026
Native DevTools/sidebar live updates verified, 2026-09-29
#13 commit 8fd14f1 extends both maintained development suites through module HMR and HTML reload in actual DevTools panels and browser sidebars. Full CI passed, including all workspace checks and both browsers' production/development suites.
WXT correction and its scope
The Firefox run exposed a native WXT problem. Its HTML rebuild group includes both
panel.htmlanddevtools.html. WXT reloaded both even when only panel output changed. Reloading the unchanged registration document removed Firefox's Devkit tab while leaving the toolbox open. It did not return after 30 seconds. Calling nativereloadPage('panel.html')alone retained the tab and replaced its document correctly.The exact-version pnpm patch now compares emitted HTML before and after a successful native rebuild:
This is illustrative pseudocode. The implementation conservatively reloads when either snapshot cannot be read. It compares output, so a shared transform that actually changes
devtools.htmlstill reloads that document. Native errors and the native reload API keep their behavior. Vite remains unpatched; there is no SDK reload/recovery controller and no new public API. No WXT upstream PR was opened.The regression test uses real WXT builds and spies on its actual
reloadPageboundary. It verifies panel-only change, changed registration output through a shared HTML transform, and missing previous output. The native Firefox observer also uses WebDriver's default page-load behavior, which fixes sidebar clicks that previously opened the sidebar and then failed inside Firefox's navigation observer.Validation and remaining gates
Direct edits to the DevTools registration document retain the native lifetime and can still require reopening the toolbox. Popup-specific HTML reload, popup/DevTools/sidebar background/config transitions, content/page updates, rapid edits and watched-production extension behavior remain open. The separate JSON-renderer routing choice still needs the owner's answer. Keep #13 open.
test(webext): verify native popup HTML reload (#13)
dvcolomban commentedon Sep 29, 2026
Actual popup HTML reload verified, 2026-09-29
#13 commit 96ace4a adds the remaining toolbar-popup HTML reload check to both native development suites. It adds tests and documentation only. The extension application and installed patches are unchanged.
The checks use
action.openPopup()and native popup enumeration, edit a disposable example source copy, and wait for the actual new document content. They do not close/reopen the popup to simulate continuity, substitute a browser tab, or introduce application test hooks. The previous module HMR, pending-action, DevTools/sidebar, background and configuration assertions still run.Chromium receipt now records 12 development groups; Firefox receipt records 8. Both complete suites pass locally, along with scoped strict Oxlint, TypeScript 7 and formatting. Full CI passed, including all workspace checks and both production/development browser suites.
One initial local run, started alongside Firefox, timed out before reaching the new popup check: the first Chromium panel stayed
Connectingfor 30 seconds. A separate Chromium rerun passed. Five comparisons against an isolated archive of the preceding commit8fd14f14all passed Chromium, so the original failure is not reproduced or classified as pre-existing. One synchronized base Firefox run instead timed out after configuration restart; its targeted rerun passed. Browser cleanup completed. Concurrent startup/restart reliability remains an open investigation; these observations do not establish a cache cause or justify another recovery mechanism. CI runs the two suites sequentially.Popup/DevTools/sidebar background/config transitions, direct DevTools registration changes, content/page updates, rapid edits and watched-production extension behavior remain open. Cross-provider JSON action routing still needs the separate owner choice. Keep #13 open.
fix(webext): isolate native browser development caches (#13)
dvcolomban commentedon Sep 29, 2026
Concurrent native cache correction, 2026-09-29
#13 commit b7ad4c9 fixes a reproduced concurrent development failure using Vite's native
cacheDirconfiguration. Full CI passed, including all workspace checks, both production browser suites, and the newly concurrent development suites.Cause and comparison
The focused probe starts Chromium and Firefox together and performs initial startup plus nine configuration restarts. Both native WXT fixtures share installed dependencies through a
node_modulessymlink. With the default shared physical optimizer cache, Chromium's first restart received HTTP 504 for@devframes_json-render-ui_renderer.js; its actual panel stayed atConnectingwith empty provider/catalog fields. Firefox completed ten starts. This occurs before the application module initializes, so RPC retry or recovery would not fix it.Changing only the native Vite cache directory to each fixture root made all ten starts pass in both browsers, without captured failures. Failed request and DOM, Chromium control, Firefox control.
Maintained change
The root separates disposable fixtures; the browser directory also separates normal Chromium and Firefox commands in one checkout. WXT already ignores
.wxtfor source watching. Vite owns optimization and invalidation. No new dependency patch, reload manager, retry loop or timeout increase was added.The maintained
pnpm --filter @devkit/example-webext test:devuses pnpm regex execution to run both complete development suites concurrently. Both passed locally, along with scoped strict lint, TS7, formatting and all 3 example tests. CI now runs the same concurrent command after installing both browsers. A local sandbox initially denied the test listener; rerunning the unchanged scoped command with local-network access passed.Diagnosis and frozen reproduction. Earlier startup/restart incidents without network captures cannot conclusively be assigned this cause. This proves the captured optimized-module failure and its correction; it does not complete the remaining reload contract. #13 stays open for the listed background/config host transitions, direct registration changes, content/page updates, rapid edits and watched production.
test(reload): verify native DevTools registration edits (#13)
test(reload): verify registration HTML and observe peers atomically (#13
test(webext): wait for native Firefox sidebar control (#13)
test(webext): wait through Firefox panel document replacement (#13)
dvcolomban commentedon Oct 1, 2026
Fixed the Firefox DevTools reload observation in commit ce546ce, following the failed full CI run. That run passed the workspace gates and debugger/browser checks before failing in concurrent native development.
The failure occurred in the existing 30-second HTML-reload poll. WXT had reloaded
panel.html; Firefox destroyed the old document's Marionette actor while the test was reading its unique marker and Connected status.panelScriptrepresented that native rejection as an assertion failure, which ended Selenium's poll immediately:The code change recognizes only the exact recorded actor-destruction assertion in the read-only reload predicate. Shared
panelScript, clicks and other effectful operations are unchanged. Unrelated script/driver errors still reject; a missing replacement still times out. No production code, dependency patch, browser recovery loop or increased native-test deadline was added.Five regression cases replay the recorded response at the real Selenium executor boundary. Before the fix, two cases fail and three controls pass. After it, all five pass, including timeout, unrelated errors, driver failure and a single failed effectful execution without retry. The unchanged concurrent native suite passed locally before editing; the original browser timing race was not independently reproduced locally, so this is deterministic reproduction of its recorded driver response, not a claim of repeated browser reproduction.
The final actual concurrent development command passes: Chromium 153.0.8010.12 completes 15 scenarios with no captured page errors; Firefox 157.0 completes 11 scenarios and confirms native stop closed its browser. Firefox's global page-error capture remains unavailable. The retained native snapshots match the final generated files. Firefox's peer observes counters 7, 8, 9 and 10 exactly; reloaded documents have fresh callers with retained provider/state. All 14 example unit tests, strict lint, TypeScript and changed-file formatting pass.
Full CI for the correction and native-auth follow-up is running. The broader reload contract remains open; this fixes its test's observation race without changing native reload behavior.
CI reconciliation, 2026-10-02: Full CI passed at
ce546ce68e7df120a1adec6141768a8ca44a24d0, including the combined changes, all production browser suites, concurrent development transitions and the executed API-evidence gate. The Firefox panel observation correction resolves the later actor-destruction failure; the earlier failed run remains historical evidence.test(reload): foreground the final native idle screenshot (#13)
fix(example): restore native renderer choice after reload (#13)
dvcolomban commentedon Oct 3, 2026
Native renderer source/style updates and restored selection, 2026-10-03
74ff82d adds maintained actual-file-edit tests on both native backends and fixes a small example startup bug.
Firefox restores the renderer selector to custom after Vite page reload. The example previously registered custom only after a change event, leaving the reference renderer behind a custom selection. A pre-fix native run reproduced the missing updated custom mount. Startup now uses the same existing native registration function as the change handler. Chromium's native reset and Firefox's native restoration remain unchanged.
Definition of Ready: native Vite already owns both update mechanisms; the existing example uses real native clients/renderer registration and can be copied for source edits.
Definition of Done for this slice:
Reproduction, ownership diagram and retained receipts. This is development CSS HMR and native module page reload. Self-accepting replacement, invalid source recovery, rapid edits, injected rendering and watched-production browser refresh remain separate cells. Firefox WebDriver Classic has no global page-error capture. No SDK reload controller or backend-state policy was added.
The full CI at e05dd25 passed. Its retained artifacts independently verify all 49 browser references, 38 distinct payloads and 414 unique native checks, plus the original 140 core, 94 runtime and 11 Devframe cases. This validates the changes described in this comment; the subsequent invalid-renderer and cleanup-callback regressions have their own pending CI run.
test(example): verify native renderer error recovery (#13)
dvcolomban commentedon Oct 3, 2026
Native invalid-renderer recovery, 2026-10-03
eb6015c extends the maintained reload fixture and both browser commands. A genuine renderer syntax error creates a new failed document, shows Vite's Oxc error overlay, leaves zero view mounts and disables the renderer selector. The same native backend remains live. Its counter advances from 2 to 3 while frontend startup is broken.
A valid source edit triggers Vite's existing reload. The fresh client reads 3, removes the overlay and mounts the updated custom renderer once. One rendered action reaches 4. The provider identity stays unchanged. Chromium resets its selector to reference; Firefox restores custom, as recorded by the prior native reload proof.
The interaction diagram, exact commands and limits link both raw receipts. The change adds no production SDK, watcher, retry or recovery controller.
Definition of Ready:
Definition of Done for this slice:
Firefox WebDriver Classic does not capture global page errors or HTTP response status. Self-accepting module replacement, rapid edits, injected rendering, backend replacement and watched-production browser refresh remain separate acceptance cells.
Downloaded CI artifacts independently pass all 49 browser references across 38 distinct receipt files and 422 referenced native checks. The retained core report has 142 passing cases; runtime has 94 and Devframe has 11. The full report accounts for 814 paths, 460 with linked execution evidence and 38 partial groups. Both native renderer recovery receipts preserve zero failed-document mounts, a live backend advancing 2 to 3, retained provider identity and a recovered UI reaching 4.