Repository navigation
State scope and recovery #8
Description
Same-host native persistence after path repair, 2026-10-04
Commit 5dd987a adds two real native-server acceptance cases, exact unit evidence links and live in-app observations. SDK, storage, adapters and browser frontend are unchanged. Devframe's existing public createStorage remains the sole persistence implementation.
Both native hosts, same two authenticated WebSocket clients:
action3 → live/peer3 → native DF0035 → physical ENOTDIR
caller repairs its owned path → no counter file at that observation
explicit next action → live/peer/file4, same connected clients/provider
direct native peer mutation → writer/file7
only afterward: close/restart → fresh native incarnation restores7
Native action completion still does not confirm disk saving. Repair is performed by the caller/test; there is no SDK retry, rollback, flush or watcher. The native storage updated listener remains registered after its caught filesystem error and handles the later mutation.
Definition of Ready
- Existing two-host write-failure tests immediately restarted clients/host and left same-owner recovery unproved.
- Public native storage implementation and concrete continued-listener behavior inspected.
- Existing real-client fixture and caller-owned disposable paths reused; no test-only production seam.
Definition of Done for this slice
- Both new cases execute 17 assertions against actual native authentication, HTTP/WebSocket transport, state replication and physical filesystem reads.
- Native DF0035 and ENOTDIR observed; failed action/state change is retained, caller obstruction remains intact until explicit repair.
- Later routed action and direct native peer mutation save successfully with both original clients connected and original provider incarnation.
- Independent caller file stays unchanged; fresh native host restores the last physically confirmed value.
- All10persistencecases and all18affectedpackageVitestcases pass, along with existing executable package checks. Strict type-aware Oxlint, TypeScript 7 and formatting pass.
- Separate live in-app check uses two existing browser pages per host and actual controls: live 1 with failed saving → caller repair → live/file 2 with original visible provider identities. Four owned tabs, two native demo processes and disposable storage cleaned up.
- Exact unit links pass the executed evidence checker. Current local total is 366 unit references; 814 paths / 460 linked / 38 gap groups and 110 browser references / 65 receipts / 621 checks are unchanged. Manual IAB observations are explicitly separate from automated browser receipts.
- Full CI for this commit completes; the last independently inspected full artifact remains 241fd1e with 364 unit references.
New tests, native ownership explanation, live observations and limitations, Devframe screenshot, DevTools screenshot.
The manual IAB check records no browser version or global page-error capture. These tests do not prove automatic retry, interrupted-write durability, crash consistency or broader supported host/mode behavior. State scope and recovery remains open. Upstream publication approvals and the assisted Chrome permission run remain separate owner inputs.
Latest state validation checkpoint, 2026-10-04
Both browsers now prove natural suspension with quiet or waiting native RPC clients, explicit fresh-client restoration, ordinary production-panel navigation during admitted work and real peer closure. Navigation/native reply-fix evidence records its two separate commits and limits. Firefox malformed-record startup and explicit recovery adds actual visible failures without overwrite, unchanged failed documents after correction alone, explicit native reload and saved state in two fresh panels. No SDK API or recovery policy changes.
Local conformance validates 110 browser references, 65 receipts and 621 exact referenced checks, with 364 linked unit-test references. Prior independently inspected full CI passed at 241fd1e. Its downloaded artifact independently verifies all 110 browser references, 65 receipts, 621 referenced checks and 364 unit-test references, including all new state slices; every linked unit case passed. The prior complete CI at 8a6e682 remains historical evidence; intervening jobs were superseded by the issue-scoped main commits. Broader state/storage and supported host-mode obligations remain open.
Combined validation checkpoint, 2026-10-02
The first attempt of combined CI at bc5baa5 passed the workspace gate and preceding Chromium checks, then timed out taking the final screenshot after the natural-idle recovery assertions. The unchanged scenario passes locally; no cause or fix is claimed from that run. Its second attempt was superseded by a main push. Full CI at 0b533e8 passed every workspace, native production, persistence, idle, quota, debugger, trust, concurrent development and executed-evidence gate. This head only removes expired dependency-age exclusions; it changes no runtime or browser fixture. The original screenshot timeout remains an unexplained intermittent failure, with no fix claimed.
Maintained native Firefox quota failure/recovery acceptance, 2026-10-02
Commit bc5baa5 promotes the bounded pilot into an actual maintained example command and CI step. It reuses the counter/panel helpers and the existing native Firefox launcher. A five-line launcher addition forwards a test-owned native preference record at startup instead of copying another process/port lifecycle. Default launch behavior remains unchanged and its natural-idle proof passes again.
Native2MiB quota → action12: both live12, saved11, visible QuotaExceededError
Remove only accepted filler keys → actual native usage decreases
Explicit action13: visible native failure, saved11
Explicit action14: live/saved14, identical provider and clients
Native runtime.reload → old panels close → fresh panels restore14 → action saves15
The runner separately checks actual quota-principal/readback, normalized native filler rejection, actual counter write rejection, both rendered status views, saved counter, independent key44 and exact provider/caller/document identities. Each action is explicitly clicked. No automatic retry, rollback or background reset obtains same-client recovery. The native reload occurs only after14 is saved.
Maintained command, source explanation and limits, runner, passing expected-sequence receipt, quota UI, same-client save.
VITE_COUNTER_STORAGE_KEY=example.persisted-counter pnpm --filter @devkit/example-webext build
VITE_COUNTER_STORAGE_KEY=example.persisted-counter pnpm --filter @devkit/example-webext exec node tests/firefox-quota.tsSet FIREFOX_BINARY when needed. The final native157 run accepted73 filler keys in76 attempts, attempted1,421,312bytes below the256-attempt/8MiB bounds, and kept the production artifact hash unchanged. The receipt passes while retaining immediateReliefPassed:false; the first post-relief failure is required evidence. Process exit and profile removal are asserted before passing. Original passed:false pilot and its separate follow-up stay unchanged.
Definition of Ready for this slice
- Native-first state/storage policy is settled; observed Firefox cleanup failure and later explicit recovery need no SDK recovery policy.
- Disposable reduced-limit profile, immutable opted-in artifact and actual storage-principal readback establish the test boundary.
- Bound filler writes and distinguish filler rejection from the smaller counter's own rejected save.
- Existing native panel/action/identity helpers provide independent live, saved and UI observations.
Definition of Done for this slice
- First maintained proof and simplified-launcher rerun both pass the full native expected sequence on Firefox157.
- Exact live/saved/UI, independent key and same-client identities are retained; native reload restores the confirmed saved value.
- Owned processes exit and disposable profiles are removed; all four actual screenshots are inspected.
- Existing default native idle still uses its unchanged30,000ms timer and proves disconnect/unmount plus explicit fresh restoration.
- Strict affected lint/types/format, all30 extension tests and the executed browser evidence gate pass. Native quota has six exact checks in its own generated receipt.
- Combined full CI passes at
0b533e8, including this evidence commit and unchanged native fixtures.
Default-size Firefox quota, interrupted physical I/O, crashes, permanent installation and the wider state matrix remain open. WebDriver Classic global page-error capture remains unavailable. Reduced quota is a native test fixture, not an SDK persistence guarantee or change to production state behavior.
Native inbound state-echo correction, 2026-10-02
Commit 3c39a79 backports the minimal native client correction through the existing exact-version pnpm patch. A received snapshot or patch previously generated a redundant write with the same synchronization ID. Delaying that actual write until the server deleted/recreated the same key reproduced replacement6→5. The earlier failing CI job did not retain packet ordering; this is a demonstrated matching cause, not a claim about that job's exact wire history.
The source diagnosis, diagram, public regression and removal conditions and upstream source candidate are reviewable. The proposed native source changes 20 production lines and removes 3. Existing public APIs, declarations, wire format, server state, renderer and serializer remain unchanged.
// Private native receive scope, not an SDK policy or public API.
function applyServerChange(state, syncId, applyChange) {
const previous = receivedServerChange;
receivedServerChange = { state, syncId };
try { applyChange(); }
finally { receivedServerChange = previous; }
}
// Existing outbound listener skips only this received state + ID.
// Newly authored writes retain the original native RPC path.Definition of Ready for this slice
- Same-key replacement regression reproduced against unchanged installed1.0.0 and inspected upstream main72d917d5.
- Native source owns the missing behavior; no SDK state mechanism or production test seam is needed.
- Ordinary local writes, synchronous nested user writes and cross-key reuse of the received ID identified as controls.
- Exact backport updates both existing native client factory copies and preserves previous patches/dependency policies.
Definition of Done for this slice
- Identical public-factory tests first fail snapshot/patch cases with expected6/received5, then pass with the installed correction; the cross-key control passes throughout.
- The maintained test uses actual native RPC over MessageChannel, queues actual I/O, and confirms the server result with an ordered native GET. It does not inspect birpc envelopes or fake a full client context.
- Local write7, synchronous callback write9 after inbound8 and cross-key same-ID writes still propagate.
- Affected adapter, native Port, server, JSON-render and extension unit suites pass. Affected strict lint, TypeScript7, formatting and both production extension builds pass.
- The real custom/reference renderer scenario passes on both native backends with no captured page errors.
- Complete rebuilt Chromium153.0.8010.12 and Firefox157.0 production extension suites pass. Firefox retains its WebDriver Classic global-error-capture limitation.
- Packed-consumer checks pass public exports, strict Bundler/NodeNext types, native hosts and browser RPC. Exact API/browser evidence gate passes with unchanged API counts.
- Proposed upstream source passes four focused native files/eight tests, native lint/types on the recorded cached source graph. No fresh-main dependency-install claim is made.
- Full CI passed at
3c39a79, including both native production browser suites.
The owner approved a separate generic bug draft after native-only reproduction. Devframe draft #422 is published at de9c518f; its description includes exact baseline/fixed commands. Both snapshot and patch cases fail with expected 6, received 5 on unchanged 72d917d5, then pass with the correction. All 18 focused source tests, native lint/type checks and the devframe package build pass on the recorded cached graph. Full upstream CI passed: lint, all Ubuntu/Windows Node 22/24/26 test jobs, end-to-end tests and Bun/Deno runtime checks. The draft remains open for owner takeover. Remove the backport when an unpatched native release passes the maintained regression and affected browser checks. Independent delayed server updates, durable writes, interrupted physical I/O/crashes and the wider native state matrix remain outside this correction.
Firefox native quota and explicit recovery investigation, 2026-10-02
Commit 4a621f3 retains an original failed pilot and a separate successful follow-up without changing production state or browser storage behavior. Both used the unchanged opted-in extension on Firefox 157.0 with a deliberately reduced native 2 MiB quota in a disposable profile. This does not measure the default Firefox quota or close the state acceptance matrix.
Confirmed saved11 → actual rendered action12 → native quota rejection, live12/saved11
Delete only test filler → first explicit action13 → native InvalidStateError, saved11
Second explicit action14 → same provider/callers/documents → live14/saved14
Only afterward: public native extension reload → fresh clients restore14 → action15 saves15
The source diagnosis and exact limitations distinguish native cleanup-transaction behavior from a permanently unusable database. The exact Firefox internal-flag attribution is source-based inference; the observed same-client recovery is directly asserted. No automatic retry, rollback, background reset or SDK storage controller is introduced.
Definition of Ready for this investigation
- Native quota preference and actual extension-storage principal/limit read back, with unchanged artifact identity and native browser APIs.
- Filler bounds distinguish rejected filler from actual rendered counter-write rejection.
- Live peers, saved records, independent key and real caller/provider/document identities are inspected.
Definition of Done for this investigation
- Actual rendered write12 fails visibly in both peers; saved11 and independent44 remain intact.
- Removing all test-owned filler lowers native estimated usage; first explicit action13 still fails with the native generic error and recorded InvalidStateError.
- A second explicit action14 saves under the exact same provider descriptor, native sender IDs, panel handles and time origins.
- Subsequent public native reload closes old panels; fresh clients restore14 and save15 under a new provider incarnation while browser/profile/add-on identity remains unchanged.
- Original receipt preserves passed:false. Follow-up receipt records passed:true and immediateReliefPassed:false. Both owned native processes exited and profiles were removed.
- Promoted in
bc5baa5to the maintained native command and exact generated receipt references described above. The original pilot receipts remain historical; the new expected-sequence receipt is the passing automated evidence. - Complete default-quota, interrupted physical-I/O, crash and remaining native state cases. WebDriver Classic global page-error capture remains unavailable.
No unresolved SDK architecture choice or owner policy decision follows from this result. The reduced-limit quota matrix and remaining native acceptance are still implementation work. The separate native state-echo correction and its upstream draft are recorded above.
Natural Firefox idle acceptance, 2026-10-02
Commit 6463b53 adds a test-only native Firefox observer and a maintained CI command. Native Firefox 157.0 stops its event page at the unchanged 30,000 ms idle timer while two native Port panels remain quiet. The observer records a suspend notification, running-to-stopped transition and disappearance of the actual background context. It attaches no toolbox and changes no production state policy, native timer, extension registration or SDK API.
Two native Port panels → confirmed storage.local counter 10
Native timer → suspend notification → background context 157 disappears
Original documents disconnect → both mounted views are disposed
Explicit fresh panels → background context 161 and new provider incarnation
Restore 10 → one rendered action → both peers and native storage reach 11
Definition of Ready for this slice
- Actual event-page status, suspend notification and default native timer identified.
- Observer runs outside the extension without attaching a toolbox or polling its APIs.
- Existing native Ports, shared state, storage and explicit fresh-client behavior reused.
Definition of Done for this slice
- Native stop observed 29,923 ms after observation began; unchanged 30,000 ms timer has no user override.
- Same process, profile, session, add-on ID, UUID and artifact hash retained throughout idle and recovery.
- Original document lifetimes retained while both clients disconnect and mounted views are disposed.
- Fresh documents and provider restore confirmed counter 10; one rendered action saves 11; independent native key stays 44.
- Ephemeral state resets and completed diagnostic work is not replayed.
- Owned browser process and profile cleanup independently verified; restored screenshot inspected.
- Strict scoped lint, types and formatting pass. Six exact receipt checks are linked to the API evidence inventory.
- Full CI passes, including this natural Firefox idle command and every maintained native browser suite.
Retained receipt, reproduction and limits. Firefox WebDriver Classic does not capture all page errors. The native shutdown warning reproduced in matched sessions without this observer and is documented rather than suppressed. This proves recovery after confirmed writes through explicit fresh clients. Pending RPC idle, interrupted I/O, crashes, quota failures and permanent installation remain separate acceptance work. The broad state ticket stays open.
Natural Chromium idle acceptance, 2026-10-02
Commit e76bbd2 adds a test-only native CDP runner, linked browser evidence and a maintained CI command. There is no production or SDK state-policy change. Playwright attaches a debugger to extension workers, preventing the desired idle observation; this runner attaches only to pages, then observes native worker target/status events.
Two native Port panels → confirmed storage.local counter 10
Quiet panels remain open → unattached worker naturally stops
Original documents disconnect → both views unmount
Explicit fresh panels → new worker/provider incarnation → restore 10
One rendered action → both peers and native storage reach 11
Ready for this slice
- Native idle inhibition from ordinary Playwright worker attachment identified.
- Observer uses an otherwise blank page and never attaches to the extension worker.
- Existing production extension build, native Ports/state/storage and explicit fresh-client policy reused.
Done for this slice
- Chrome/153.0.8010.12 naturally destroyed the actual worker after 30,011 ms with two quiet native Port clients open. Native stopped status independently agrees.
- The worker remained unattached, and the extension build hash remained unchanged. No force-stop, extension reload or timer change occurred.
- Both original documents kept their time origins, disconnected and disposed counter/management views.
- Fresh native callers restored confirmed counter 10 under a new worker and provider incarnation, each with one mount per view.
- One rendered action saved 11 in both clients and native storage. Independent key 44 survived; ephemeral input/execution state reset; completed actions were not replayed.
- Actual page-exception and worker-error observations are empty. Native browser cleanup exited the owned process and removed its disposable profile.
- Strict scoped lint, TypeScript 7, format, 25 affected unit tests and linked API-evidence gate pass. The first restricted unit run failed at the HTML fixture's denied loopback bind; the authorized native-server rerun passed all 25.
- CI runs and retains the native idle scenario. Full CI passed, including the natural Chromium idle command and every maintained native browser suite.
Retained receipt and restored UI, reproduction and limits. Five exact checks are linked to the existing Port lifecycle group. The public API-path count is unchanged.
This proves natural Chromium idle after confirmed writes and explicit fresh-client recovery. It does not prove pending-RPC idle, automatic reconnect, interrupted writes, browser crash or Firefox natural suspension. The broad state ticket remains open for those native evidence gaps.
Status reconciliation, 2026-10-02
State policy is accepted; remaining work is native lifecycle evidence. Native shared-state ownership, direct native writes and application-owned persistence are implemented. Maintained evidence covers both server hosts, actual extension Ports, peer updates, view teardown/republication, forced Chromium worker termination, normal Chromium restart, explicit Firefox extension reload stable Firefox full-browser restart with explicit native temporary fixture loading, and natural Chromium/Firefox background idle with explicit fresh-client recovery. No new generic state API or recovery policy is awaiting approval.
Commit bfd62d3 adds a genuine stable Firefox 157 restart test against one disposable native profile. The first process exits through native driver.quit() before the new process starts. Different Firefox process IDs, WebDriver session IDs and document lifetimes are recorded, alongside the same profile, extension ID and UUID. The second session does not copy storage or rewrite the UUID.
first native session: install temporary fixture → two clients → action/native state writes
confirm storage.local counter 10 and independent value 44
native quit → assert first process exited
new native session, same profile → explicitly install temporary fixture
fresh provider and documents → restore 10 → one rendered action → both clients/storage 11
All six native checks passed locally. Two new clients mount one counter and one management view each under a new provider incarnation. Counter 10 restores, the next action reaches 11 exactly once in both clients and storage, independent data stays 44, and diagnostic executions reset from 1/1 to 0/0. Retained receipt, restored UI, reproduction and installation boundaries.
This slice's readiness and completion evidence:
- Stable Firefox, the native profile owner, explicit temporary fixture loading and confirmed-write boundary are identified.
- Both processes, fresh documents/providers, shared native state and independent data are asserted through the real browser.
- Scoped build, strict lint, types and changed-file formatting pass. The executed API gate links all six receipt scenarios.
- CI runs this native restart command against its pinned stable Firefox and retains the generated receipt/screenshot.
- Natural Chromium worker idle and Firefox event-page idle after confirmed writes have native acceptance evidence and passing CI.
- Crashes/interrupted writes, Firefox quota failures and permanent signed installation/automatic extension availability have acceptance evidence.
Stable Firefox rejects permanent installation of this unsigned fixture with ERROR_SIGNEDSTATE_REQUIRED. The successful restart test explicitly loads the temporary fixture in each session; it does not claim automatic extension availability. Native persistence follows confirmed writes only. WebDriver Classic's global page-error capture limitation remains explicit. No SDK persistence or recovery mechanism was added.
Combined full CI passed at bfd62d3d12c5713541bad31ec23ed66d0ab50de1, including the genuine stable Firefox restart, every maintained native browser suite, concurrent development transitions and the executed API-evidence gate; the preceding full CI passed at bcd79213ea47a11ae13d0e923e75e71e1a5e7359. This issue and its broader coverage gates stay open. Earlier dated checkpoints record historical evidence and do not reopen the accepted state contract.
Normal Chromium browser restart, 2026-10-02
The committed native restart proof closes Chromium and reopens the same profile and unpacked extension path. Two fresh clients restore saved counter 7 under a new provider incarnation; one rendered action updates both clients and storage to 8, while an unrelated key retains 44. All seven Chromium persistence checks and scoped lint/types/formatting pass, with zero page errors. The initial CI attempt failed in a bundle-test timeout before browser installation. The reproduced diagnosis and two-line correction are recorded under Release and conformance contract. Full CI for the combined follow-up passed at f11b7153badd4ad19be5b6786371f89ac98503d1, including the new Chromium restart check, all maintained native browser suites and the final executed API-evidence gate.
This closes the normal Chromium restart evidence gap after a confirmed write. Natural suspension, crashes, interrupted writes, Firefox browser restart and broader lifecycle behavior remain open. No SDK persistence API or recovery mechanism was added.
Firefox native persistence reload, 2026-10-02
The native Firefox proof, committed as 9b26a03, verifies that public chrome.runtime.reload() closes both old extension tabs. Fresh tabs restore the confirmed saved counter under a new provider incarnation; the next action reaches both peers and storage. Independent data survives and diagnostic execution state resets. No production runtime change was needed.
The live Firefox 157 check, all 24 affected tests and strict lint/types/format pass. Full CI on the combined final state passed at 34e301f77bb9edfd767419e1506855f0ce00b3cb, including workspace validation, both native persistence tests, every production browser suite, concurrent development transitions and the combined executed API-evidence gate. The prior Chromium implementation passed its full CI below. Natural suspension, browser restart, interrupted writes and Firefox quota behavior remain unproved. WebDriver Classic's missing global page-error capture stays explicit.
Native extension persistence, 2026-10-02
Implementation 7a32dcd adds opt-in native storage.local to the runnable extension example. Chromium verifies restoration after a confirmed write and forced worker termination, independent keys, visible real-quota failure without rollback/retry, and invalid-data rejection without overwrite. Native RPC initialization uses its existing resolver option; no SDK storage or recovery API was added.
Strict lint/types/format, all 24 affected tests, both default/persistent browser builds, default Chromium/Firefox native suites and both development suites pass. Full CI passed for the exact persistence commit, including all production and concurrent development suites. Commands and exact evidence. Firefox live persistence, natural suspension, browser restart and interrupted writes remain unproved.
Native server persistence, 2026-10-02
The implementation and executed evidence, committed as bdc3651, adds opt-in native createStorage to both runnable server hosts. Eight real HTTP/WebSocket cases verify saved-value restoration, peer updates, independent paths, caller-owned files, invalid data and actual write failure. Two live in-app tabs per host confirmed saved values restored under new incarnations and subsequent updates reached both peers and files.
Build, strict lint/types/format, all four executable checks and 16 affected tests pass. Full CI passed for the exact commit, including workspace validation, every production browser suite, both concurrent development suites and the executed API-evidence gate. Native asynchronous write semantics stay unchanged; action completion does not promise persistence. The default stays ephemeral. No SDK storage abstraction or recovery policy was added. WebExtension storage, natural suspension and broader lifecycle cases remain open.
Background connection ownership and native termination, 2026-10-01
Implementation 5abe64aa11e0bebd51a0f09b48cbea76c5ff566a fixes an example ownership bug: the background Port's disconnect callback used the same routine as whole-page teardown. Losing that one provider also disposed independently authenticated server connections and their views.
The page now has separate background and whole-page cleanup paths. Background loss closes its native RPC, mirrored state, provider adapter and two local renderer mounts. The client and independent native server connections remain usable. Explicit Disconnect, pagehide and HMR still dispose everything the page owns, including when background cleanup already ran. Startup checks dispose late local mounts against the background lifetime. No reconnect, retry, persistence layer or new SDK API was added.
page owns
background Port -> extension state / feature / management mounts
native Devframe connection -> its state and views
native DevTools connection -> its state and views
background termination -> close background-owned resources only
explicit page teardown -> close every page-owned connection
Local validation passed with TypeScript 7, strict type-aware Oxlint, Oxfmt, both extension builds and the three existing example unit/bundle checks. Actual Chromium production passed 52 scenarios with zero page errors; Firefox production passed 51. The concurrent development suites passed 15 Chromium and 11 Firefox scenarios, including native module/HTML/background/configuration transitions.
The maintained worker test verifies:
- The exact extension worker reports
running → stopping → stopped, then its debugging target disappears. - An already-started RPC rejects and the two still-open documents lose only their background-owned mounts.
- Both independently authenticated server views and actions remain usable. Their counters advance independently to 2 and 5; all-realm broadcast includes the known extension's rejection alongside both successes.
- Explicit Disconnect after background loss closes both surviving server connections; retained detached buttons cannot change server state.
- Explicit fresh page connections start a different background incarnation with counter 0 and zero prior executions. Each page mounts exactly one counter and management view; one action updates both to 1.
Recorded native observations · Commands and limitations. CI also retains the generated worker receipt and screenshot with its existing API evidence artifact.
The runtime uses the existing native disconnect event. The CDP worker controls belong only to the test. Forced native termination does not prove Chrome's natural idle-suspension timing, Firefox event-page suspension, arbitrary storage recovery or rollback of external side effects. State in this example is intentionally ephemeral; persistent application state remains contribution/host-owned.
The architecture and glossary clarify the already accepted distinction between one backend connection and its containing surface. The broader state/lifecycle obligations remain open. Full CI passed workspace validation and all production browser suites, then failed in Firefox development while opening the native sidebar (toggleSidebar: button was null). The final combined evidence gate was skipped. The scoped test correction waits for the actual Firefox sidebar control before one native click; Firefox production and both concurrent development suites pass locally. 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.
Reconciled status, 2026-09-30
Native Port state synchronization, peer writes, view unmount/reopen and surface reload have executed in Chromium and Firefox. See the maintained extension receipts, packages/webext/tests/shared-state.test.ts, and 1ef4f90.
Native state lifetime and write policy are settled. No generic state-recovery or enforcement protocol is required. Scripted background reload does not prove spontaneous worker suspension/restart. That case, untested navigation/storage failures and any explicit persistence promises remain open.
Earlier checkpoints below are dated evidence. Their then-pending items are superseded by this status and later accepted resolutions; they do not reopen settled architecture choices.
Native JSON renderer evidence, 2026-09-28
The renderer delivery now includes commit 1e45712 with native view/state/action integration on both server hosts, five automated tests and live two-tab mount/unmount/disconnect confirmation. Commands and limitations. Extension surfaces, cross-provider rendering and automated browser conformance remain open; no new state or rendering framework was introduced.
Accepted direction, 2026-09-28
The owner confirmed: stay close to native behavior on both backends; the SDK eases portability and must not become a complex enforcement layer. This settles the previously pending Q3 and Q4:
- Preserve native host state lifetime. A portable-provider replacement does not reset state owned by its surviving native host.
- Preserve native write behavior and authorization. Contributions can use native mutations or typed actions; the SDK does not impose action-only writes.
- Contributions and hosts choose keys, resource scope, validation, persistence, migrations and conflict handling through native facilities.
The earlier mandatory provider-session scope, incarnation-prefixed keys, single-authoritative-writer and generic revision/replication proposals are superseded. They are not implementation requirements. The original working-example and supported-host conformance requirements remain.
Part of Design a portable contribution SDK and WebExtension runtime. Canonical definitions live in Native state ownership and GLOSSARY.md.
Question and resolution boundary
How can clients share contribution state through native backends while preserving each host's state ownership and lifecycle?
Use the public native state API directly. Add adapter code only for a demonstrated transport or lifecycle gap. State defaults are now settled. This ticket remains open for extension and renderer integration evidence, not further approval of the rejected enforcement proposals.
Context and current behavior
The same contribution can run in a Devframe host, a DevTools host, or eventually an extension host. Several UI documents can observe one native host. Each action still returns a typed result to its caller; native state updates let other clients observe changes without adding a second command-response correlation protocol.
A provider incarnation pins action/capability execution. It is not a state namespace. A native host can outlive one portable provider. Multiple portable providers within that host use its native key space; contributions use distinct keys when they need separate records. Independent hosts retain independent values under identical keys.
flowchart LR
ClientA["Client A"] -->|"typed action"| Provider["Portable provider"]
Provider --> Service["Contribution service"]
Service --> State["Native host state"]
ClientA -->|"native mutate / patch"| State
State -->|"native updates"| ClientA
State -->|"native updates"| ClientB["Client B"]
Host["Native host lifecycle"] --> State
Replacement["Provider replacement"] -.->|"old action binding invalidated"| Provider
Both paths follow the host's native authorization. The diagram does not imply that an arbitrary page is an authorized extension client; actual page/extension sender checks remain under Permissions and trust.
Implemented API sketch
These calls use the maintained browser example's native client. They are not a proposed SDK state facade:
const state = await nativeClient.sharedState.get<{ value: number }>(counterStateKey);
const unsubscribe = state.on('updated', value => render(value.value));
render(state.value().value);
// Native writes stay available under the host's policy.
state.mutate(value => { value.value += 1; });
// A contribution can instead express a validated use case as an action.
const result = await client.actions.invoke({
action: increaseCounterAction,
input: { amount: 1 },
});
showCommandResult(result);
// The document owns this subscription, not the native host's record.
unsubscribe();The example attaches its authenticated provider before reading state and omits client initialValue to await the native initial read. Native values/types, patching and synchronization remain upstream-owned. This flow does not claim atomic snapshot/subscription ordering for every concurrent write. Native mutate is not a backend-completion promise. TypeScript state types are not runtime validation.
Ownership and retention
| Event or concern | Accepted behavior | Evidence / remaining work |
|---|---|---|
| Surface closes | Dispose all connections/mounts it owns; native host state follows its own lifetime | Two-client server checks, native extension closure/reload and explicit teardown after background loss |
| One backend connection closes | Reject through that connection and dispose its mirrors/mounts; keep independent connections usable. A fresh client reads current native state | Both native server hosts survive extension background loss; no automatic reconnect or replay |
| Portable provider is disposed/replaced | Old action bindings reject; surviving native state and observers are retained | Both server hosts, real sockets and live browser checks |
| New in-memory native host | Counter fixture starts at its initial value | Both server hosts; persistence must be explicit |
| Providers share one host | Native key space is shared; contribution/host chooses distinct keys when isolation is wanted | Native API ownership; no synthetic incarnation prefix |
| HMR / built asset reload | Retention follows which native owner actually survives | Existing Vite lifecycle tests; full renderer/reload proof remains in the reload ticket |
| Browser document navigation | Document-owned listeners/agents must end with the real document | Extension proof remains; no core target-generation protocol |
| Extension worker restart / extension reload | Follow actual browser/native lifecycle and any explicit application storage | Forced Chromium termination, native reload, Chromium restart, explicit Firefox restart, and natural Chromium/Firefox idle have evidence for confirmed-write restoration; interrupted work and permanent Firefox installation remain open |
| Persistence, migrations, edit conflicts | Native/application policy; expose actual failures where that feature is provided | No mandatory SDK revision, storage or migration layer |
| Cross-provider synchronization | Explicit contribution behavior using its operations/native facilities | Broadcast does not imply replication, merge or atomic transactions |
Authoritative permissions and catalog membership belong to their owning APIs, not an ordinary client-writable shared-state key. A readonly TypeScript wrapper would not establish write authority. The native client also forwards updates through set/patch, so blanket denial must not be introduced as a shortcut.
Delivered example and verification
Native observation implementation supplies the browser observer, separate command result, disconnect/reload controls and owned cleanup. Native lifetime follow-up records the accepted policy, adds replacement to the example launcher, and brings tests/state.test.ts to six real-socket tests on both native hosts.
The tests cover native client writes, peer updates, separate hosts under the same key, unsubscribe, fresh-client recovery, provider replacement with surviving observers, rejected stale action bindings, and a fresh host's initial value. They consume built package exports and actual native sockets.
Live in-app confirmation on both hosts: two tabs display 1; terminal replace changes the provider incarnation without resetting state; the old action rejects; reloading a peer reads 1 under the successor; its increment returns 2, also observed by the old tab. All temporary tabs and processes were closed. Detailed report.
Run the example:
pnpm --filter @devkit/example-server-contexts demo:browser devframe
pnpm --filter @devkit/example-server-contexts demo:browser devtoolsUse the printed loopback URL. Type replace in the launcher terminal to exercise replacement; any other input stops the demo. Earlier two-tab live checks also cover disconnected stale display, reload, peer closure and actual native host shutdown.
Remaining implementation scope
- Extend the existing actual Port/state and native JSON renderer proof to remaining supported execution/surface combinations. Port synchronization and renderer disposal are implemented; content/page authority remains with Permissions and trust. Use supported upstream entry points for any remaining integration.
- Verify interrupted native I/O, pending RPC suspension, crashes and navigation cases beyond the recorded closure/reload scenarios. Natural Chromium worker idle and Firefox event-page shutdown now have direct evidence for confirmed writes and explicit fresh connections.
- Use explicit contribution-owned storage in any example claiming persistence and demonstrate its native failure path; no universal storage policy is required.
- Record tested host behavior and unsupported cases in the API proof inventory. Test SDK adapter boundaries and promises rather than reproducing all upstream state tests.
These requirements do not authorize a competing store, codec, patch format, replication engine, automatic recovery supervisor or write-enforcement framework.
Dependencies
Contribution and realm contract defines portable lifetimes. Provider discovery and routing owns selection and current provider identity. Permissions and trust owns actual boundary authority. Renderer and surface contract and Live preview and reload contract consume the accepted native ownership rules. They can progress against those settled rules without inventing additional state policy. Examples and API coverage contract owns the complete evidence standard.
Definition of Ready
- The owner selected native lifetime and native write policy.
- The native public state API and the concrete integration boundary are identified.
- Two real server hosts and two clients provide a runnable comparison.
- Application persistence/conflict policy is separated from SDK portability obligations.
Definition of Done
- Canonical architecture and glossary record native state ownership, writes and retention boundaries.
- Both real server hosts prove peer observation, explicit fresh-client recovery, native writes and provider replacement with surviving state/observers.
- Runnable browser example and live checks confirm the server behavior; strict affected type/lint/format checks and six socket tests pass.
- Forced Chromium worker termination rejects pending work, preserves independent native server connections, allows full explicit teardown and starts a fresh provider with reset ephemeral state without replay.
- Actual Chromium/Firefox extension transports and lifetimes are exercised, including worker restart, navigation, closure and native failures for supported state/storage features.
- JSON renderer bindings use native state and release owned subscriptions on unmount/reload.
- Every supported adapter/state integration promise has a runnable example and success/failure evidence in the coverage inventory. Unsupported cases are explicit.
Decision record
The owner's 2026-09-28 decision supersedes the earlier enforcement proposals. No further input is needed on Q3/Q4. Keep this ticket open until remaining native integration evidence is delivered; a settled policy does not imply completed extension conformance.
Activity
dvcolomban commentedon Sep 22, 2026
Upstream reuse audit resolution observed released shared-state patching, sync-ID deduplication and unsubscribe. A real Node MessageChannel probe also showed explicit RPC
$closerejecting a pending call and removing listeners. Automatic browser Port interruption and worker recovery were not tested.Acceptance: disconnect a real extension Port with a pending operation and active subscription; require a settled operation and removed listeners, then reconnect without stale or duplicate delivery. Define provider separation, storage authority, schema/version handling, mutation conflicts and worker-restart recovery independently of in-memory patch synchronization. Shared-state implementation, RPC channel implementation.
dvcolomban commentedon Sep 22, 2026
Research handoff and acceptance gates
Sources: Browser capability audit.
Accountable owner:
dvcol, until reassigned. These observations inform this open contract; they do not resolve it.Native background recovery remains unrun; debugger activity and inspection can keep Chromium workers alive. Toolchain observations of a new Firefox background generation and retained storage are useful but do not establish natural idle termination, SDK recovery or a causal reload policy.
Acceptance additions:
feat(examples): observe native state across clients (#8)
dvcolomban commentedon Sep 27, 2026
1ca4d3e implements native state observation in the maintained server browser example. There is no new SDK state API or upstream patch. The current counter comes from native state updates; the initiating action's typed result is displayed separately.
Live in-app browser confirmation passed on both Devframe and DevTools: two tabs observed
0 → 1, a disconnected peer stayed at1and was labelled stale while the other reached2, reload recovered2with the same incarnation, and closing the first tab left the other able to reach3. An independent host retained its own value. Actual DevTools host shutdown marked the open view stale and disabled actions. Temporary test tabs and demo processes were closed.Four native socket tests cover both hosts, including native client mutation, peer observation, separate host values, unsubscribe, fresh-client recovery and independent ownership. Existing built-export integration/routing/auth checks and the browser bundle check pass. Scoped builds, TypeScript 7, strict Oxlint and Oxfmt pass. Full repository CI passes.
The checked-in report records the native APIs, diagram, live receipts and limits. This proves explicit reconnection using a new client, not automatic recovery, persistence, atomic snapshot/subscription ordering or extension conformance.
The issue body contains the next owner frontier with context, options and pseudocode: Q3 native-host lifetime versus per-incarnation reset; Q4 native write policy versus enforced action-only writes. Both remain pending. No state-policy answer is inferred from the example's existing native behavior.
test(examples): preserve native state ownership (#8)
dvcolomban commentedon Sep 28, 2026
Recorded the owner's native-first decision in commit 7242a3a, now on main.
Q3 and Q4 are settled: preserve native state lifetime and native writes. Contributions/hosts own keys, scope, validation, persistence and conflicts. The portability layer adds no mandatory reset, action-only enforcement, storage or replication protocol. The architecture/glossary and this ticket now reflect that boundary; rejected enforcement proposals are removed from active requirements.
Two new socket tests bring the native state suite to six on actual Devframe/DevTools hosts. They verify state and observers survive provider disposal/replacement while old action bindings reject; a fresh attachment continues from the retained value and a fresh host starts at zero.
The browser launcher now accepts
replace. Live in-app confirmation on both hosts: two tabs at 1; replacement changes incarnation and the old action rejects; reloaded peer reads 1 and increments to 2; the old tab's native observer also displays 2. All four test tabs and both hosts were cleaned up.Validation: affected example type checks, strict type-aware Oxlint, Oxfmt, six socket tests and live browser checks pass. Full workspace CI passed. No SDK runtime layer or upstream patch was added.
This ticket remains open for actual extension lifecycle/transport and JSON-renderer integration evidence. Those are implementation obligations; Q3/Q4 no longer need owner input. Detailed report.
fix(webext): preserve server connections on background loss (#8)
feat(examples): retain counters with native server storage (#8)
dvcolomban commentedon Oct 1, 2026
Native server persistence example, 2026-10-02
Implemented in bdc3651. Both runnable server hosts can retain the shared counter through Devframe's public
createStorage. The existing service, portable actions, client selection and state observation remain unchanged. Default state remains ephemeral.The example publishes the native store under its existing key before installing the counter service. The caller owns the supplied file; host cleanup removes only its temporary host directory. Use a separate file for each independent live host.
Executed evidence
Eight new real HTTP/WebSocket tests run against both native hosts through built package exports:
DF0012warning, initial value 0, unchanged file until a later mutationDF0035logs the write failure, obstruction stays intact, fresh host starts from 0The in-app browser confirmation used two tabs per host and separate files. Devframe reached 2 while DevTools stayed at 1. Both files were read and checked before stopping their hosts. Restarting the commands restored 2 and 1 under new provider incarnations. Another action reached 3 and 2 in each peer and its file. All four temporary tabs and both demo processes were closed.
Browser observations, Devframe screenshot, DevTools screenshot, maintained tests, commands and semantics.
Affected validation passed: build, strict type-aware Oxlint, TypeScript 7, Oxfmt, all four executable package checks and 16 Vitest tests, including the six existing ephemeral-state and two custom-kind tests. No full-repository validation ran locally. Full CI passed for this exact commit: workspace build/types/strict lint/format/tests, every production browser suite, concurrent Chromium/Firefox development transitions and the executed API-evidence gate.
Scope and remaining work
This is contribution/host-owned persistence using the native API, with no new SDK storage facade, auth policy, recovery controller or dependency patch. Native writes are asynchronous and currently debounced by 100 milliseconds. An action result is not confirmation of a successful disk write. The public native store has no flush/dispose API; shutdown does not promise to flush or cancel pending writes. Abrupt process exit, concurrent writers, file watching, migrations and cross-provider replication remain outside this example's contract.
This proves observed-write restoration in both server hosts. WebExtension storage, natural worker/event-page suspension and the wider navigation/failure matrix remain open. The ticket is not complete.
20 remaining items
test(state): maintain native Firefox quota recovery proof (#8)
dvcolomban commentedon Oct 2, 2026
Maintained native Firefox quota failure/recovery acceptance, 2026-10-02
Commit bc5baa5 promotes the bounded pilot into an actual maintained example command and CI step. It reuses the counter/panel helpers and the existing native Firefox launcher. A five-line launcher addition forwards a test-owned native preference record at startup instead of copying another process/port lifecycle. Default launch behavior remains unchanged and its natural-idle proof passes again.
The runner separately checks actual quota-principal/readback, normalized native filler rejection, actual counter write rejection, both rendered status views, saved counter, independent key44 and exact provider/caller/document identities. Each action is explicitly clicked. No automatic retry, rollback or background reset obtains same-client recovery. The native reload occurs only after14 is saved.
Maintained command, source explanation and limits, runner, passing expected-sequence receipt, quota UI, same-client save.
VITE_COUNTER_STORAGE_KEY=example.persisted-counter pnpm --filter @devkit/example-webext build VITE_COUNTER_STORAGE_KEY=example.persisted-counter pnpm --filter @devkit/example-webext exec node tests/firefox-quota.tsSet
FIREFOX_BINARYwhen needed. The final native157 run accepted73 filler keys in76 attempts, attempted1,421,312bytes below the256-attempt/8MiB bounds, and kept the production artifact hash unchanged. The receipt passes while retainingimmediateReliefPassed:false; the first post-relief failure is required evidence. Process exit and profile removal are asserted before passing. Original passed:false pilot and its separate follow-up stay unchanged.Definition of Ready for this slice
Definition of Done for this slice
bc5baa5; currently running.Default-size Firefox quota, interrupted physical I/O, crashes, permanent installation and the wider state matrix remain open. WebDriver Classic global page-error capture remains unavailable. Reduced quota is a native test fixture, not an SDK persistence guarantee or change to production state behavior.
test(state): verify native Firefox idle during pending RPC (#8)
dvcolomban commentedon Oct 4, 2026
Natural Firefox suspension with an admitted native RPC
1b91b09 extends the accepted native state/lifetime proof without changing SDK or production behavior. The new runner reuses the existing lifecycle observer, production extension, panel fixtures and explicit fresh-client recovery. Its successful receipt is written only after observer removal, native process exit and disposable profile cleanup.
The final Firefox157 run observed native stop 30,045ms after observation began, with the unchanged30,000ms timer and no toolbox, force-stop, extension reload or timer override. Native background context disappeared while process/session/profile/add-on identity remained. The caller showed
[birpc] rpc is closed, cannot call "probe:wait". Fresh provider incarnation and document identities are asserted separately from reset caller counters. Native storage reads confirm10 before suspension and11 after the explicit action; the immutable production artifact hash is retained.Maintained command and limits, runner, seven-check receipt, and restored UI are committed. CI runs this direct command; protected manual-permission files remain untouched.
Set FIREFOX_BINARY if needed; geckodriver0.37.1 is pinned in CI. The test controls real native I/O and observes browser lifecycle; it adds no test-only production signature or automatic recovery controller.
Definition of Ready:
Definition of Done for this slice:
The pending probe has no external side effect. This proves connection rejection after native owner loss, not remote cancellation, rollback, interrupted physical storage durability or crash recovery. Temporary add-on installation and Firefox Classic global-error-capture limits remain explicit. The broader state ticket stays open. Continue with the matching native Chromium observation and remaining document-lifetime cases; no owner API decision is required by this result.
test(state): verify native Chromium idle during pending RPC (#8)
dvcolomban commentedon Oct 4, 2026
Native Chromium pending-work suspension
8a6e682 adds a narrow
--pendingmode to the existing native idle runner. It reuses the production extension, page/CDP observer, counter preparation and fresh-client recovery. It does not attach to the worker or alter native keepalive. Both modes now publish successful receipts after owned process/profile cleanup; their output filenames stay separate.Chrome153.0.8010.12 passes the pending scenario twice and its original quiet-idle control once. The final pending run observed natural worker destruction after30,005ms, zero attached workers, zero page exceptions and zero worker errors. Artifact hash stays unchanged. Fresh worker target, provider incarnation and document lifetimes are asserted independently. The native pending error is
[birpc] rpc is closed, cannot call "probe:wait".Command, behavior and limits, eight-check receipt, and restored UI retain the actual native result. Its existing runner has one new admission step and observes the old caller's rejection; there is no new SDK or production mechanism.
Definition of Ready:
Definition of Done for this slice:
Both browser hosts now have separate quiet-idle and pending-native-RPC suspension evidence. The waiting probe has no external side effect. This result does not promise remote cancellation, rollback, interrupted physical storage durability or crash recovery. Remaining document-lifetime, native-failure and broader supported-mode acceptance stays open. Manual Chrome permission work remains untouched; no new owner API decision follows from this slice.
fix(state): suppress native RPC replies after closure (#8)
test(state): verify native panel navigation and peer closure (#8)
dvcolomban commentedon Oct 4, 2026
Native navigation and reply-after-close checkpoint, 2026-10-04
Two separate main commits complete this slice:
Chrome originally reported an unhandled disconnected-Port error after the admitted handler completed. The native diagnosis and pinned source candidate reproduce both late success and late failure using native RPC only. Unchanged installed native code and current birpc source fail; the guard passes. Function-error reporting and normal open replies remain intact. This generic correction belongs in birpc, followed by Devframe adoption/rebuild. No new upstream PR has been opened.
Definition of Ready for this slice
Definition of Done for this slice
241fd1epassed. Independent inspection of its downloaded artifact verifies both native-navigation receipts and all linked checks; this run supersedes the intervening jobs.Commands and limits, Chromium receipt, Firefox receipt.
The prior full CI at
8a6e682is successful and independently verified against its downloaded conformance artifact: 107 browser references, 62 receipts, 599 exact checks and 362 unit-test references, all linked results passing. That older run does not validate these new commits.This ticket remains open. Ordinary production navigation is now proved; back-forward cache, other supported modes, native interrupted-write/crash behavior and permanent Firefox installation retain their separate limits. Firefox Classic cannot capture global page errors. This guard does not claim to fix closure during asynchronous transport posting. Native storage remains example-owned rather than an SDK durability guarantee.
test(state): verify native Firefox malformed storage recovery (#8)
dvcolomban commentedon Oct 4, 2026
Native Firefox malformed saved-state checkpoint, 2026-10-04
Commit 32c91fe adds a native production Firefox command and its CI step. It reuses the existing launcher, state and panel fixtures and changes no production API. The inventory also now links all seven existing Chromium persistence receipt checks, including its native quota, malformed-record and clean-restart proof, rather than only its two worker-restoration checks.
Definition of Ready
Definition of Done for this slice
241fd1epassed. Independent inspection of its downloaded artifact verifies this native-browser receipt and all linked checks; this run supersedes the intervening jobs.VITE_COUNTER_STORAGE_KEY=example.persisted-counter pnpm --filter @devkit/example-webext exec node tests/firefox-invalid-storage.tsUse the production artifact built with the same key and set
FIREFOX_BINARYif required. Commands, source and limits, receipt, failed panel UI, restored UI.Local conformance now validates 110 browser references, 65 receipts, 621 distinct referenced browser checks and 364 linked unit-test references. The API counts stay 814/460; 38 groups retain gaps. Interrupted physical I/O, crashes, permanent Firefox installation, broader host modes and Firefox global error capture remain separate limits. This does not close the broader state ticket.
docs(state): validate native birpc reply fix with upstream tooling (#8)
dvcolomban commentedon Oct 4, 2026
Native birpc source validation, 2026-10-04
Commit b8c958d records the unchanged source candidate with fresh upstream-native validation, an exact receipt and a reviewable draft description. It changes only research evidence; the installed correction and SDK APIs are unchanged.
A disposable checkout of
antfu-collective/birpcate62dda59installs its unchanged frozen lockfile using declared pnpm 11.21.0. The native graph uses Node 26.9.0, Vitest 4.1.10, ESLint 10.8.1, TypeScript 6.0.3 and tsdown 0.22.14. No package-manager verification rule, manifest or lockfile was changed.Applying only the two new tests to unchanged source gives two expected late-post failures and eight passing close/error/resolver controls. Restoring the one closed-flag guard passes the identical ten cases. Changed-file native ESLint, native package typecheck/build and diff checks pass. Source and patch hashes are retained in the validation receipt.
The handler still completes once, and a handler failure still reaches
onFunctionError. The guard covers closure while awaiting a resolver or handler, not every asynchronous transport-post race. It adds no cancellation, retry, rollback, SDK routing or public API.Ready: generic native reproduction, minimal source/test candidate, upstream-native focused tests/lint/types/build and draft description. Pending: explicit owner approval before opening a new upstream draft. No birpc PR has been published.
Full downstream CI already passed at 241fd1e; independent artifact inspection validates all 110 browser references, 65 receipts, 621 checks and 364 unit-test references. CI for this documentation-only commit passed. Full upstream CI has not been run or claimed.
test(state): verify same-host persistence after path repair (#8)
dvcolomban commentedon Oct 4, 2026
Same-host native persistence after path repair, 2026-10-04
Commit 5dd987a adds two real native-server acceptance cases, exact unit evidence links and live in-app observations. SDK, storage, adapters and browser frontend are unchanged. Devframe's existing public
createStorageremains the sole persistence implementation.Native action completion still does not confirm disk saving. Repair is performed by the caller/test; there is no SDK retry, rollback, flush or watcher. The native storage updated listener remains registered after its caught filesystem error and handles the later mutation.
Definition of Ready
Definition of Done for this slice
New tests, native ownership explanation, live observations and limitations, Devframe screenshot, DevTools screenshot.
The manual IAB check records no browser version or global page-error capture. These tests do not prove automatic retry, interrupted-write durability, crash consistency or broader supported host/mode behavior. State scope and recovery remains open. Upstream publication approvals and the assisted Chrome permission run remain separate owner inputs.