Skip to content

Proposal 095: Node UI Interaction Layer

Based on:

Status

Proposed. This document plans implementation; it does not claim a working Stimulus integration or measured usability improvement.

Date

2026-10-09

Executive Summary

Add a small, reusable interaction layer above HTMX, not a replacement frontend. Keep server-rendered HTML and HATEOAS, the optional Tauri host, and daemon-owned authority. Prefer Stimulus without Turbo, conditional on the compatibility gate below, to organize local behavior around existing HTML fragments.

The first slice preserves a conversation while opening, navigating and closing a detail panel. Its purpose is lower cognitive load and stronger human agency: the interface preserves context without deciding domain outcomes for the user.

Context and Problem Statement

node:node-ui/README.md already describes modal slots, focus and transition helpers; node:node-desktop/README.md separates the user-facing /app window from the browser-first operator console. Consolidate those mechanics rather than introducing parallel implementations. Conversational clients inspire continuity of interaction, not claims about their internal architecture or dependencies.

Current implementation baseline (inspected 2026-10-09)

Facts observed in the Node repository on this date; P095-001 re-verifies them before any migration:

  • HTMX 2.0.8 is vendored together with response-targets and the SSE extension ([email protected]). SRI appears on an SSE include, but not consistently across layouts; the user base also loads SSE without SRI.
  • Interaction behavior lives in hand-written global scripts: orbiplex-modal.js (82 lines), orbiplex-user.js (626), orbiplex-user-transitions.js (86), orbiplex-question-deadline.js (26) and orbiplex-csrf.js. They attach document/body listeners and timers that are mostly live for the document lifetime, without a shared per-fragment lifecycle. Some timers are released: chat polling stops after detecting a detached history, fetch timeouts are cleared, and the question countdown stops on expiry. Global delegation alone is not a leak; inventory each owner's acquisition and cleanup.
  • The user conversation is a modal (user/fragments/contact_chat_modal.html) rendered into #orb-modal-slot, and orbiplex-user.js opens it through its own fetch, outside HTMX. A detail panel opened "beside" the conversation cannot satisfy the proposed non-modal workspace contract without a layout decision. The separate chat-history polling fetch also overlaps HTMX/SSE refreshes; a manual fetch alone does not prove duplicate request ownership. Where the conversation lives is therefore a first-slice decision, not a detail (P095-009).
  • The operator console and the /app shell navigate with hx-boost and hx-push-url. No hx-history attribute or htmx.config override exists. The vendored 2.0.8 implementation uses sessionStorage for htmx-history-cache. Inference, not a runtime observation: its enabled default snapshot cache can therefore already retain rendered operator, identity and consent pages. This exposure is independent of Stimulus and is addressed first (P095-007).
  • Node UI responses carry Content-Security-Policy: frame-ancestors 'none' only; there is no script-src. The desktop host configures Tauri with csp: null and withGlobalTauri: true, and base templates contain inline scripts. Script in the same JavaScript context can access the exposed IPC API; this is not proof that every command is authorized. Window/webview permissions and host command checks remain separate. Current middleware paths return HTML through Html(...) and fragment | safe; normalize_server_html_fragment rewrites presentation and is not an HTML sanitizer. Package admission is not fragment isolation. P095-008 must establish a checked boundary, not assume one.
  • The desktop proxy's FORWARDED_RESPONSE_HEADERS omits CSP, Cache-Control and Vary; server-side header changes alone do not reach the custom-origin document. The bootstrap embeds /app in an iframe, so blindly forwarding frame-ancestors 'none' would also break its current handoff.

Baseline evidence: node:node-ui/static/htmx.min.js, node:node-ui/static/orbiplex-user.js, node:node-ui/templates/user/base.html, node:node-ui/src/handlers/ui_surfaces.rs, node:node-ui/templates/middleware/surface.html, node:node-ui/src/security.rs, node:node-desktop/tauri.conf.json and node:node-desktop/src/main.rs (FORWARDED_RESPONSE_HEADERS).

Proposed Model and Decisions

1. Preserve ownership

Owner Responsibility
Daemon/services Domain state, authorization, validation and execution
Node UI + HTMX Server representations, links/forms, requests, swaps and URL history
Interaction controllers Panel visibility, focus, scroll and bounded ephemeral presentation state
Tauri under P052 Native windows, menus and explicitly authorized host integration

Local presentation state is legitimate; a second domain state machine is not. Controllers must not infer grants, advance workflows or replay mutations on reconnect. No daemon credentials reach client JavaScript. A visible action is not authorization; its owning service checks current authority when invoked.

2. Bound the workspace

V1 has a stable main surface, one detail panel and the existing modal slot. Opening details does not replace the conversation/composer. Panel navigation uses server-provided links and HTMX requests; close restores focus to the opener or a documented safe fallback. A bounded, in-memory panel back stack holds only same-origin route references, not HTML, grants or form contents, and refetches on return. Panel steps do not write browser history; HTMX alone owns top-level URL history. Full-page GET representations remain available for those links.

Preserve unsent text during panel operations and transient disconnection; never automatically submit it. Warn before intentional navigation that would discard it. Full-reload/crash recovery of drafts is not promised by this slice. Streaming updates respect a user who has scrolled away from the end and expose an explicit return-to-latest action rather than forcibly scrolling.

Command palettes, resizable layouts and extra native windows are future clients of these mechanics, not prerequisites. Arbitrary docking, an SPA router, a UI description language for plugins and a native-client rewrite are non-goals.

3. Stimulus and HTMX can complement each other

The upstream contracts are compatible: Stimulus attaches behavior to HTML and observes DOM changes; HTMX requests and replaces HTML. This is a design inference from their documented mechanisms, not a completed Orbiplex integration test.

  • Start one Stimulus application per document. Let its DOM observer discover swapped controllers; do not rescan/restart it after every HTMX swap.
  • Stimulus lifecycle callbacks run asynchronously. htmx:afterSwap is not proof that a newly inserted controller is connected; use controller/target connection callbacks for initialization and an explicit handshake if cross-controller readiness is needed. Do not encode timing with arbitrary sleeps.
  • Make connection/reconnection idempotent. Release controller-owned listeners, timers and subscriptions on disconnect. A stable shell can keep presentation state while its child targets change; DOM replacement does not preserve a new element's controller instance automatically.
  • Each action has one request owner. Controllers may trigger an existing HTMX action, but must not also fetch/submit it. Do not introduce Turbo Drive/Frames or a second history router. Late responses must not reopen a closed panel or overwrite a newer navigation; cancel or reject superseded reads.

Compatibility sources, inspected 2026-10-09: Stimulus introduction, lifecycle, HTMX integration, HTMX events.

4. Privacy, authority and extension boundaries

Ephemeral focus, panel and scroll state stays in memory and is cleared on logout/identity change. Do not persist conversation text, receipts or sensitive HTML in browser storage. Disable HTMX history snapshots for sensitive surfaces with hx-history="false"; verify that the pinned version and custom protocol behave as intended. Navigation restoration always preserves current server-side authorization and never revives an expired approval.

Durable behavioral preferences belong behind P091's file-backed contract, not a parallel localStorage settings database. Until that owner supports a setting, keep it session-only or defer it. No broad P091 completion dependency is needed for the ephemeral first slice.

The middleware HTML extension model remains supported, subject to an explicit compatibility review of existing executable surfaces. The host owns slots and allowlisted controllers; module HTML is not permission to register executable controllers, address unrelated shell targets or invoke Tauri commands. Preserve existing admission, session and CSRF boundaries, and establish the fragment isolation missing from the baseline before exposing shared controllers to module content. Namespaced data attributes are conventions, not security isolation.

Trade-offs

Stimulus offers lifecycle structure without replacing HTML rendering, at the cost of a dependency and asynchronous lifecycle coordination. Plain JavaScript remains the fallback if the spike adds more complexity than it removes. Alpine is an alternative for local reactive widgets, not a second library to add alongside Stimulus in this slice. No performance advantage is claimed without measurement. Plain-JS fallback and UI rollback must retain the independent P095-007/008 privacy/security hardening; rollback never re-enables unsafe snapshots or scripts.

Failure Modes and Mitigations

Failure Required evidence
Repeated swaps duplicate handlers or requests Repeated mount/unmount/reconnect produces one action and no residual subscriptions
Out-of-order response changes the wrong view Delayed reads, redirects/triggers and OOB effects after close/new navigation/identity change are rejected before effects
Details erase drafts or scroll Preserve unsent text and intentional scroll; failed Back retains the previous entry; edits after discard consent require fresh consent
Modal loses keyboard users Focus containment/return, Escape, labels and keyboard traversal work; reduced motion is respected
Back/logout exposes another identity's content Old-cache cleanup, cache-disabled history restore, bfcache/pageshow and multi-window identity-transition tests; offline restoration stays locked
UI replay revives approval or repeats a write Expired/refused/ambiguous outcomes stay explicit; no automatic mutation retry
Middleware activates privileged shell behavior Reject scripts, declarative action/target injection, same-origin script sources and response-header/OOB escapes; host commands still enforce authority

Implementation Recommendations

  1. Inventory orbiplex-modal.js, orbiplex-user.js, transitions, CSRF helpers and current HTMX extensions first. Reuse #orb-modal-slot; assign one owner per behavior and retire overlapping listeners when migrating it.
  2. Keep controllers with node-ui/static/ and contracts/examples in node:node-ui/README.md (longer material in its docs/). Use explicit host-owned registration, small controllers and declarative HTML bindings.
  3. Pin and serve Stimulus locally with version, license and integrity recorded; no runtime CDN or mandatory new frontend build pipeline. Check compatibility against the repository's actual HTMX/extensions and CSP, not just latest docs.
  4. Scope shortcuts away from text editing and IME composition. Distinguish a non-modal detail panel from a modal focus trap. Announce loading/errors without reading every streamed token to assistive technology.
  5. Prefer real DOM/browser tests for lifecycle and history. Retain a Tauri custom-origin run separately; browser success alone is not WebView acceptance. Compare startup, interaction latency and resource use with the same baseline.
  6. Keep runnable acceptance beside node:tools/acceptance/ harnesses and results under node:docs/evidence/. Update the Node implementation ledger and relevant MVP rows only as actual adoption/evidence changes; regenerate derived views.

Workspace slot topology

The host shell owns three slots. Fragments never create slots; they fill one.

<div class="orb-workspace" data-controller="orb-workspace"
     data-orb-workspace-max-entries-value="8">
  <main id="app-main" data-orb-workspace-target="main">
    <!-- conversation history, composer -->
  </main>
  <aside id="orb-detail-panel" role="complementary"
         aria-labelledby="orb-detail-title"
         data-orb-workspace-target="panel" hidden></aside>
</div>
<div id="orb-modal-slot" class="orb-modal-slot"></div>
  • #app-main keeps the existing hx-select/hx-push-url navigation and is the only target that changes top-level URL history. Moving it inside the stable workspace wrapper must also move its replacement templates: one live main and panel ID after every swap, with the wrapper outside the replaceable subtree.
  • #orb-detail-panel is non-modal: no focus trap, no aria-modal, Escape closes it only when focus is inside it, and the main surface stays operable.
  • #orb-modal-slot stays the one modal owner for confirmations and short forms. A detail panel never opens inside a modal; a modal may open above a panel.

Every panel link keeps a full-page representation in href and a fragment request in hx-get. HTMX remains the only request owner; hx-sync cancels a superseded panel read before a controller has to reason about it:

<a href="/app/evidence/{ref}"
   hx-get="/app/evidence/{ref}"
   hx-target="#orb-detail-panel"
   hx-swap="innerHTML transition:false swap:0ms ignoreTitle:true"
   hx-push-url="false" hx-select="unset" hx-boost="false"
   hx-select-oob="unset"
   hx-sync="#orb-detail-panel:replace">Evidence</a>

The server selects the representation from HX-Request: true and HX-Target: orb-detail-panel, never from a query flag, and answers both forms with Vary: HX-Request, HX-Target and Cache-Control: no-store on authority, identity, consent and conversation surfaces. The fragment root is fixed:

<section class="orb-panel" data-orb-panel-fragment
         data-orb-panel-href="/app/evidence/{ref}">
  <h2 id="orb-detail-title">…</h2>
  …
</section>

data-orb-panel-href must match the normalized requested path and permitted query, not an arbitrary response URL. Reject credentials, scheme-relative URLs, unowned routes and authority-bearing query values. These endpoints are illustrative, not implemented. Representation headers never grant authorization. The panel title comes from its heading; it does not change the document title.

Fence the entire panel response, not just its final target: disallow HX-Redirect, HX-Location, HX-Refresh, URL push/replace, retarget/reswap, unapproved trigger headers and out-of-band swaps. The host maps authentication loss separately to a locked shell. response-targets, inherited hx-select and response headers must not redirect panel output into the composer or modal. Keep V1 swaps immediate; delayed/view-transition commits require a fresh ordering check at the actual DOM commit. hx-sync alone does not cover these effects.

Ephemeral presentation state

Controller instances hold these values in memory only; nothing is written to localStorage, sessionStorage, IndexedDB or the URL:

PanelEntry = { href: same-origin path, at most 2048 bytes,
               opener: element id or null }
PanelState = { entries: PanelEntry[0..8], epoch: opaque context token,
               generation: non-wrapping safe integer, open: bool }
TailState  = { pinned: bool, unseen: u16 }
DraftState = { dirty: bool, revision: non-wrapping safe integer }
                                      # per composer element; holds no text
  • One owner hook at htmx:beforeRequest admits the panel read, increments generation and captures {epoch, generation, href} plus the opener in a local WeakMap keyed by XHR before sending. Derive the opener from the request source element or explicit host-owned context, not the ordering of Stimulus and HTMX click listeners; an unready owner cancels the request. Close/context invalidation advances the fence independently. No wire header is necessary. The bundled 2.0.8 does expose requestConfig.headers on response event details, but local ordering need not become a server-facing protocol. Rotate the epoch on identity/context change or counter exhaustion; never reuse it when controllers reconnect.
  • Reject stale, closed or wrong-epoch responses with preventDefault() at htmx:beforeOnLoad, before response headers cause effects. beforeSwap is too late for HX-Redirect/HX-Trigger and extensions can change its swap decision. Retain a final exact-slot/epoch check; close/disconnect also aborts in-flight reads. Abort alone cannot retract a response already being processed.
  • Back refetches the previous href; commit the pop/push only after the current response is accepted. On failure retain the prior entry and expose the error. Eight entries are an upper bound; an accepted ninth drops the oldest.
  • The composer text stays in the DOM element that the panel never replaces. DraftState drives discard confirmation before replacing that composer, using htmx:confirm. Bind consent to its revision and recheck before destructive swap, so typing while navigation is pending cannot silently lose new text. Attach beforeunload only while dirty; it is best-effort, not a guarantee for browser history or native-window closure. P095-004 must cover those paths explicitly. It never copies, persists or resubmits the text.
  • TailState.pinned is true while the history scroller is within 32 px of its end, measured before content growth. When new messages arrive unpinned, the controller increments unseen (saturating at 65535, displayed as 65535+), counting message identities rather than tokens or repeated snapshots, and reveals a "Jump to latest (N)" button. A polite live region announces at most one count change per two seconds, never streamed tokens.
  • Logout, identity change/lock and orb:local-unlock-required invalidate pending work, clear all four states, remove prior identity content including composer text, and close the panel. Privacy invalidation is not vetoed by a dirty-draft prompt. Unlock begins a fresh context, never restores an old epoch's draft.

Controller catalog and registration

V1 registers a closed set of host controllers from one static file, node-ui/static/orbiplex-controllers.js, that calls Application.start() once per document:

Identifier Owns Migrates from
orb-workspace panel open, close, back, opener focus return, PanelState new; panel logic only
orb-modal modal focus containment, Escape, backdrop close, focus return orbiplex-modal.js
orb-follow-tail scroll pinning, jump-to-latest, TailState chat-history code in orbiplex-user.js
orb-draft-guard dirty flag and discard confirmation, DraftState new
orb-shortcuts global keys, suppressed in editable fields and during IME composition (event.isComposing) new behavior for existing footer hints

CSRF stays a non-controller global script because it must cover every request, including ones issued outside any controller. Stimulus connects only registered identifiers, so unknown data-controller values in fragments stay inert. That is not sufficient isolation by itself, because an untrusted element inside a host controller's scope could still name its actions:

  • Before DOM insertion, a parser-based admission policy must strip scripts, event handlers and unauthorized behavioral attributes (Stimulus controller, action, target, outlet and value bindings; existing host data-* hooks; HTMX executable hooks, target/history/OOB escapes and unsafe URLs). Allowed module links/forms stay within their host-mediated mount. Filter response control headers too. This policy is new work, not an existing sanitizer.
  • Mark the admitted subtree with a host-owned [data-orb-untrusted] boundary; host controllers reject actions from it, including synthetic event paths. Closing is a host-owned control outside that subtree, not a blanket exception. Event checks alone do not prevent injected Stimulus targets or values.
  • Same-context executable module code cannot be isolated by these attributes or CSP alone. If an existing module needs such code, explicitly treat it as trusted host code or keep it outside the privileged shell under a separately reviewed isolated surface. Do not silently break it or claim it is sandboxed.
  • No controller wraps the Tauri bridge. Native integration stays in its own host-owned script under P052.

Browser hardening that precedes the controller layer

Sensitive-view amendment — accepted 2026-10-18

JavaScript is required only for representations containing secrets or sensitive unredacted data, including recovery/secret-entry views, private message/personal data and private evidence. Operational redacted configuration (D1), component status and ordinary navigation retain full no-JS representations. A synthetic marker on an operational page proves cache restoration, not that its real data belongs to a sensitive class.

Sensitive views close by construction, not JavaScript detection: a document navigation returns a data-free shell and explicit guidance that this view needs JavaScript to protect data, with CLI/desktop alternatives. Only a separate, current-authorized no-store request may return sensitive content. The client installs its lifecycle/context guard before that request, clears sensitive nodes on pagehide, and reacquires or keeps an empty shell after persisted pageshow. Do not hide already-delivered secret HTML with CSS or place it in HTML/JSON data islands, URL parameters, browser storage or a second settings store.

Node owns a closed route/template sensitivity catalogue, documented beside its implementation. Adding a sensitive view requires a catalogue entry and shell, authorization, no-JS, restoration and late-response tests. Undeclared sensitive representations refuse rather than fall back to ordinary full HTML. No raw biosignal HTML view is admitted by the current delivery; a future such view must enter this catalogue before exposure. Explicit attachments/CLI exports remain separate authorized operations, not an erasure promise for downloaded files.

Defense in depth retains private no-store/Vary, short UI sessions and cache clearing on logout. Subsequent accepted clarification, 2026-10-18: do not use Apple private API/SPI or weaken the Rust unsafe prohibition for supplementary cache controls. This replaces the earlier engine-opt-out recommendation. Managed windows require JavaScript and public nonpersistent storage; disabled-JS or persistent Tauri input configuration refuses startup. Qualify actual WebView configuration and lifecycle behavior separately. BFCache is not disabled; neither is HTTP cache in memory. Nonpersistent storage is not a BFCache opt-out or an OS forensic-erasure promise. Missing platform evidence remains explicit and cannot replace the data-free shell boundary. No property of an arbitrary browser is an authorization claim.

The closed catalogue currently covers identity secrets/secret entry, participant readiness inside configuration (a separate fragment, never the operational configuration document), key entry, participant private Messaging/contacts/notifications/issuance/outbox, private case reasons, operator message content and notifications, raw contact and relationship data, inquiry/execution/Corpus content and exact delegation material. The exact route/template bindings and negative tests belong to node:node-ui/src/private_views.rs; native unlock is a separate host-owned shell. Redacted operational status, configuration, component metadata and private-evidence references alone do not silently broaden this catalogue. A declared sensitive template on an unbound route must refuse; secret-entry template drift is tested.

These changes reduce existing exposure, need no Stimulus and can land first:

  • Disable HTMX history snapshot storage globally, for example with <meta name="htmx-config" content='{"historyCacheSize":0,"refreshOnHistoryMiss":true}'> (both option names are confirmed in the vendored 2.0.8 source). Load before HTMX starts. Remove the known htmx-history-cache key from current sessionStorage and legacy localStorage at upgrade and identity invalidation, together with the owned htmx-current-path-for-history metadata; never clear unrelated origin storage. Missing-history navigation reloads under current authorization. Per-surface hx-history="false" is an extra guard.
  • Treat HTMX snapshots, HTTP cache and browser back/forward cache as different mechanisms. no-store and the HTMX options are not a universal bfcache ban. Cover pageshow restoration, other open windows and offline failure with a locked/redacted state until the current identity is revalidated. Record actual browser/WebView results, including absence of a stale-content flash before revalidation (redact before leaving where required). Do not promise erasure of already exported copies.
  • Add matching integrity metadata for vendored HTMX, response-targets, SSE and the selected Stimulus asset in every owning layout. SRI checks bytes, not the trust of a same-origin script supplied by a module.
  • Move inline scripts and event-handler attributes to static handlers, then enforce a tested CSP (including script-src 'self', and a reviewed nonce/hash only where unavoidable). Inventory HTMX eval-dependent features and remove or replace them; do not enable unsafe-eval/unsafe-inline to silence breakage. Pin an explicit policy for allowEval and allowScriptTags after that migration. Same-origin script URLs must still pass fragment admission.
  • P095-007/008 also own desktop forwarding or host reconstruction of cache and script policy. Cover bootstrap, /app iframe and settings documents without accidentally breaking framing or allowing arbitrary embedding.
  • Decide withGlobalTauri independently of IPC authorization: removing window.__TAURI__ does not remove __TAURI_INTERNALS__ or isolate scripts sharing /app. Audit all registered commands, caller window/webview/frame context, arguments and permissions in the host. A private wrapper is not a security boundary. Keep P052's trusted-shell/native-command boundary explicit.

Additional references: HTMX history, Tauri capabilities, Tauri configuration, beforeunload limitations.

Test structure

  • Keep the pure parts as plain modules with no DOM access: panel stack push/pop/limit, generation comparison, tail pinning arithmetic and the draft confirmation predicate. Test them with Node; the existing node:tools/test-node-ui-question-deadline.cjs demonstrates the lightweight runner but mocks DOM access, so it is not itself a pure-module example.
  • Test lifecycle, history and focus in a real browser through one pinned driver (for example Playwright) recorded as an optional development dependency in P095-002. Keep native WebView evidence separate. Direct tauri-driver supports Windows/Linux, not macOS; on macOS select a pinned test-only embedded driver (such as the documented WebdriverIO Tauri service) or retain explicit manual evidence. Browser WebKit is not proof of the app's WKWebView/IPC behavior. See Tauri WebDriver support.
  • Serve interaction fixtures only from test builds, never from a production route; exclude embedded drivers/test IPC from release artifacts too. Label any mocked backend and untested platform explicitly in the evidence.

Implementation Tracker

Separate service-control deliveries — adopted 2026-10-18

Operation-result lookup amendment — adopted 2026-10-19: first-pair previews display the opaque owner approval reference before submission. After a lost response or reopened/restarted UI, current authorized operators use a separate GET lookup form/link, without resubmitting the plan or renewing old UI review tokens. Full pages remain no-JS. The panel admits only the closed two-setting result resource with one canonical bounded reference; GET read forms and POST mutation routes remain disjoint. Its existing eight-entry volatile breadcrumb stack may retain an opaque result-read href, never plan/receipt/declaration or authority; close/context invalidation clears it. Unknown, pending and terminal do not establish non-execution; a historical committed result does not establish current application/readiness. Retention uses P091's existing bounded journal, not a UI history store. Owner unavailability stays a separate read failure.

History-pressure/result-recovery amendment — adopted 2026-10-19: configuration details and the uncertain/error path link to the owner's recent retained results for that setting. A lost browser response or UI restart must not require copying the reference from the preview; exact-reference GET remains a second path. Full pages remain no-JS, the panel retains only bounded read hrefs, and neither client resubmits a mutation, renews an approval or treats an empty/unknown result as non-execution. Result support is presentation-catalogue data, not a repeated template allowlist or an authority grant.

Setting details show owner history pressure. A disabling preview explicitly discloses any export/checkpoint/prune summary before its single approval; it must not hide archival behind a direct or ordinary write. P091 owns reservation, durability, pending/head protection and quarantine. UI rendering neither changes owner evidence nor performs maintenance on GET.

Each first-pair preview has an inert bounded correlation id and closed owner phase timings, including an explicit refusal. Only that projection, never source values or arbitrary owner prose, reaches diagnostics. Client/harness observation treats an admitted approval and a terminal refusal/unavailable response as different terminal states; waiting only for an Approve button cannot turn a known refusal into an unexplained timeout. A preview refusal is not an uncertain mutation. Controlled contention and a separately counted normal cohort are causal/frequency evidence for their conditions only. Without the older response/trace, the original Logging outlier remains unattributed; later successes do not explain it.

Implementation/evidence checkpoint (2026-10-19): the scoped archive/recent-result and controlled preview flows pass real no-JS/browser-panel/application-WebView trials; 30 fresh Logging previews per client have owner ids/phases. The final unchanged images also pass D1 21, lookup 14+14/native13, reviewed pair15/native14 and private browser6/native trusted restores3. One hardening run failed its global unhandled-JS-error gate and four later trials did not reproduce it. Thus the campaign is not clean, historical outliers are not explained, and no broader P095/release promotion follows. Full source/image/candidate and failed-trial accounting is in node:docs/evidence/configuration-service-control/HISTORY-PRESSURE-PREVIEW-20261019.md.

The managed WebView must not execute slow preview I/O on its custom-protocol callback. A narrow public asynchronous responder serves only strict, unapproved first-pair preview forms through one worker with at most eight queued jobs. Saturation refuses before UI contact. The preview freezes its cookie context, cannot publish cookies or follow an identity redirect, and is discarded if that context changes. Recovery, approval/commit and identity transport retain their existing ordering. This is transport responsiveness, not a new authority, retry policy or permission to move arbitrary mutations onto background workers. Real WebView qualification must retain both refusal traces and responsiveness under the same controlled owner delay, without merely extending observation deadlines.

Presentation review amendment — adopted 2026-10-19: refused startup sources preserve diagnostic full-page, including explicit CLI full-page, with a visible source refusal and separate conservative launch fact, not a resolved default. Explicit panel still requires admitted sources. Repair without UI restart leaves the fallback launch unconfirmed; valid/current-refused sources respectively offer edit-source/repair-source. Neither fallback nor repair changes authority, service configuration, receipts or lifecycle state. This bounded repair is done on a separate frozen candidate: actual browser no-JS and application WKWebView exercise refusal/repair/confirmed restart; pair profiles pass 15/14 assertions, D1 21, hardening 10 and private browser 6/native trusted restores 3. Pins and review evidence belong in node:docs/evidence/configuration-service-control/PRESENTATION-REFUSAL-REVIEW-20261019.md. That checkpoint did not implement the next lookup row or promote broader task/platform status.

Next bounded follow-up Status Completion gate
Operation-result lookup by approval reference done; first-pair scope only P095-004/005 clients and P091 owner adapters pass no-JS full-page 14, browser panel 14 and actual WebView profile 13, native exit 0. Exactly four approved POSTs/two completed-response losses per profile; GET after UI restart/later writes and held-writer CLI reads do not repeat effects. Concurrent-tab proof is browser-only; forced review expiry is an owner-unit case. Unknown never proves non-execution or renews consent. Shared bounded retention is explicit.
Historical Logging preview and older unclassified scheduler 409s attribution unavailable from retained evidence Controlled current mechanisms are reproduced/fixed; missing historical response/trace/reason is not reconstructed or marked explained.
History pressure, recent results and controlled preview diagnosis done in the first-pair feature scope; campaign not clean External false before persistence, visible explicit archive, recent per-setting GET after completed-response loss/UI restart and 30 fresh previews per no-JS/browser-panel/actual WebView profile pass. See the dated Node history-pressure report; whole proposals/general retention stay partial.
Diagnose the hardening JS error scoped mechanism repaired and qualified; unique historical attribution unavailable Controlled old-loader network failures reproduce undefined rejection in 3/3 Chromium trials and real XHR aborts reproduce it in 3/3 WKWebView trials, with clean controls. The fixed loader/deadline pass browser 12/native 6, D1 21, hardening 10 normal/fault, primary pair 15/14 and private browser 6/native restores 3. Missing historical phase/stack is not reconstructed.
Strict-Clippy maintenance and current-base integration done in the bounded static/code/first-pair scope Final Node test-only source a7bc322b passes four strict profiles without suppressions, code22, fresh D1 21 and the unfiltered gate: 7868 passed/0 failed/94 ignored, none filtered. Actual browser15/WebView14, lookup14+14/native13, hardening/private/restoration and bootstrap smoke retain runtime source 0cd7b647 on verified byte-identical production images. The closed Task-Pack v1 projection preserves grammar, authority and safety reserve. A separate device-free fixture observer counterexample and retained-entry correction preserve all time/TTL limits; the original unphased timeout remains unassignable. See node:docs/evidence/configuration-service-control/CLIPPY-INTEGRATION-20261019.md; failed evidence and broader P091/P095/P096 or release limits are unchanged.

The 2026-10-19 private-loader follow-up owns the initial guarded GET's Promise and real source-configured eight-second XHR deadline. It never suppresses global errors, retries a read/unknown mutation, weakens admission or restores a retired shell. Current-context refusal keeps the existing data-free invalidation view. The source/asset-pinned old/fixed cohort uses read-only JS/SRI substitution; primary-app WebView rollback and sensitive BFCache restores are separate from auxiliary fault-probe windows. Browser sensitive BFCache admission is not observed. Public native APIs suffice; no SPI, unsafe exception or release fixture surface is added. The original missing stack prevents unique incident attribution. See node:docs/evidence/configuration-service-control/PRIVATE-LOADER-JS-20261019.md. This closes the scoped defect, not P095-004/005/007, broader UX/platform gates or release readiness, and does not relabel an earlier failed campaign.

The result-lookup candidate also passes common-image D1 21, pair browser 15/native profile 14, hardening 10 and private browser 6/native trusted restores 3. See node:docs/evidence/configuration-service-control/OPERATOR-RESULT-LOOKUP-20261019.md for source/image pins, real downstream-response-loss injection, review corrections and the per-file candidate map. These are focused local delivery proofs, not conversation/accessibility/platform, historical ergonomics or release closure.

Durable rollback amendment — adopted 2026-10-19: the first-pair Node UI presentation is ordinary node_ui.operator_presentation, with bundled full-page default, lexical file overrides and a final explicit CLI override. Only UI restart/fresh client navigation applies a new value; the setting owner reports current resolution independently from its active launch presentation. Desktop proxies that UI rather than owning another store or launcher flag. Rollback/re-enable retains P095-007/008 and never undoes service decisions, receipts or lifecycle facts. Manual file editing and read-only inspection are the bounded adapter scope. Qualification includes repeated flag-free starts and browser/actual WebView previews during active S020, separate from the paused exact-interleaving scenario. The old invocation-only checkpoint below remains historical evidence, not current operator guidance. See node:docs/evidence/configuration-service-control/DURABLE-ROLLBACK-20261019.md. This follow-up slice is done on the reviewed common images: actual browser and application WKWebView pass durable rollback/re-enable, source/current-active inspection and twelve previews each while periodic S020 remains enabled. Independent D1, hardening and three trusted native private-view restores pass. The deterministic busy-guard regression, not a green rerun, establishes lock independence. Whole task/platform/conversation statuses below remain partial.

P091 and P095 share the Node service-control delivery contract. D1 qualifies full pages for Messaging enabled and logging.level: real browser preview, file commit, application or explicit waiting, result, refusal and recovery. D2 adds the component/configuration detail panel using the same owner contracts after P095-002 and P095-007/008, then separately qualifies the actual desktop WebView. Full-page D1 evidence does not complete P095-001/009. The first D2 pair is the component view and configuration detail; moving the conversation is outside this delivery. P095-001/009 retain their slot, history, focus and no-panel-in-modal gates for that pair. A rejected Stimulus spike uses the documented plain-JS fallback with unchanged owner contracts.

One linked acceptance report records separate D1/D2 status and source revisions. Use the same operator scenario before/after; qualitative observations state sample size and bias. D1 pages require full navigation, CSRF, no-store and isolation from untrusted middleware fragments before exposure. D2 retains every history, CSP/IPC, response confinement and WebView proof in this tracker.

Statuses: todo, partial, done, blocked. Rows initially started todo; upstream documentation compatibility is not runtime completion. P095-007 and P095-008 are framework-independent hardening steps. The isolated test-only spike may precede them; production controller adoption may not. P095-001 to P095-005 and P095-007 to P095-011 form the first slice; P095-006 gates broader reuse rather than expanding it silently. Hardening is not evidence that the workspace is complete.

Scoped implementation checkpoint — 2026-10-18: Node has separately passing D1 no-JS browser/CLI/runtime and D2 component/configuration browser/native owner flows, including native identity-change, Back and unavailable-owner checks. The native compatibility spike and actual caller/argument refusals are distinct from owner/file/process evidence. The shared report records reviews/corrections, fixture and actual boundaries, restoration details, the original cache non-observation, missing chronological ergonomic baseline and platform limits. The subsequent closeout adds actual trusted restoration in full Chrome configuration pages and application WKWebView HTTP/custom-protocol top documents. The subsequent sensitive-view amendment keeps operational D1 no-JS while preventing sensitive document navigations from acquiring secret HTML. Pinned native trials include a real one-time seed acknowledgement and trusted BFCache restoration after normal navigation, protection-context change and owner stop. A controlled suppressed-animation baseline reproduces a sufficient native bootstrap stall; navigation no longer depends on animation callbacks and reveal waits for iframe load. This does not reconstruct the exact OS trigger of the older coarse timeout. See node:docs/evidence/configuration-service-control/RESTORATION-BOOTSTRAP-20261018.md; the earlier closeout and failures remain immutable. Rows remain partial where their broader gates are not established by this first pair.

First-pair final closeout — 2026-10-19:

Delivery gate Status Exact scope
D1 full pages/CLI done Messaging enabled and daemon logging.level: 21 actual no-JS browser/owner/file/process-or-pending/restart/recovery cases on the final common images
D2 component/configuration pair done Same owner contracts in browser and actual application WKWebView, forced S020 interleaving and UI-only full-page rollback/re-enable; independent current privacy/IPC regressions

The final Node report records the common four-image set, all failure/development capsules and scoped review corrections. Rollback retains independent P095-007/008 hardening and preserves owner files, completed commit relations, receipt and application facts; it does not undo a write. The current native panel Back did not observe BFCache admission; three separate sensitive-view trials observed trusted persisted restores with the seed absent. The initial asleep-display attempt is blocked, not a bootstrap failure; fresh actual qualification follows user unlock. Closed commit diagnostics, crash-fixture preflight and public fixture pause/idle/resume do not change production policy or repeat uncertain effects. An earlier missing commit response is not explained by the later pass. This closes the agreed first-pair delivery gates for P095-001/004/005/007/009/010, not every task row below. Conversation/drafts/scroll, broader accessibility/IME, other-platform and release campaigns remain partial. Historical baseline is unavailable-closed and existing baseline/v1 is retained without an improvement claim or a new bootstrap population sample.

ID Status Depends on Task and completion gate
P095-001 partial — Inventory helpers; select an existing conversation/detail pair and freeze slot, history, draft lifetime and trust boundaries in the Node UI contract. Record listener/timer inventory per script, baseline measurements and rollback path. 2026-10-18: First pair explicitly changed to component/configuration; stable slots, lifetime/trust inventory and rollback contract are recorded in Node. Historical baseline is unavailable-closed; a pinned scripted baseline/v1 is frozen for future comparisons only.
P095-002 partial 001 Spike pinned Stimulus + HTMX in isolated browser/Tauri fixtures: swaps, reconnect, errors, history, SSE and exactly one request per action. Verify both library initialization orders, early XHR fencing before redirects/triggers and target confinement despite response-targets/OOB. Exercise the source-confirmed history options. Record adopt/reject, driver/platform and mock boundaries; retain plain-JS fallback if rejected. 2026-10-18: Browser and actual WKWebView fixtures pass both orders, 20 swaps each, reconnect, SSE, early response fencing and native IPC checks; the final fixture actually loads response-targets and retains native results separately.
P095-003 partial 002, 007, 008, 009, 010, 011 Implement the controller catalog and conversation/detail/return slice using the pure state modules; preserve composer, scroll and focus; retire duplicate legacy handlers, including separate chat-open and overlapping chat-history fetches. 2026-10-18: The first pair has one Stimulus application with workspace/modal owners, bounded pure href state and final DOM-commit fencing. Conversation/composer/scroll migration and retiring its independent history fetch remain out of scope.
P095-004 partial 003, 007, 008 Retain executable evidence for every failure row, including identity change, malicious fragments, stale authority and delayed responses; verify keyboard/IME, accessibility and reduced motion. 2026-10-18: Real owner/browser/native primary flows and browser privacy/injection/refusal cases pass. Native identity-change, Back and unavailable-owner checks also pass; later actual BFCache admissions are retained for pinned Chrome configuration and WKWebView top documents. The later sensitive-shell amendment and controlled bootstrap fix have separate proofs; final unchanged UI/Desktop/owner binaries passed three trusted sensitive restores, policy/unlock readback, three suppressed-animation starts and seven native D2 cases with an awake display. Broader accessibility/IME and platform gates remain open. 2026-10-19 scoped follow-up: Recent result GET after completed-response loss/UI restart needs no remembered reference or repeated mutation. The native bounded public preview worker preserves context and remains responsive under a controlled 5.3-second owner refusal. Same-image pair browser15/native14 and private browser6/native restores3 pass. One hardening run failed its global JS-error gate; four later passes do not explain it, so the campaign is not clean and broader task status is unchanged. Private-loader follow-up (2026-10-19): The scoped undefined-rejection mechanism is now reproduced/fixed with pinned old/current assets and final browser/native regressions; unique attribution of the original missing trace remains unavailable. See the Node PRIVATE-LOADER-JS-20261019 report. Earlier campaign failures and broader partial gates are unchanged.
P095-005 partial 004 Verify browser and desktop task completion and bounded resource use against baseline; test rollback without changing domain state; synchronize Solution 001, Node UI docs, ledger and applicable MVP evidence. 2026-10-18: Primary full-page and panel tasks pass real owner writes and controlled restart. Historical baseline is unavailable-closed and current baseline/v1 is frozen without an improvement claim; broader platform/rollback campaign remains open. 2026-10-19 scoped follow-up: Recent result GET after completed-response loss/UI restart needs no remembered reference or repeated mutation. The native bounded public preview worker preserves context and remains responsive under a controlled 5.3-second owner refusal. Same-image pair browser15/native14 and private browser6/native restores3 pass. One hardening run failed its global JS-error gate; four later passes do not explain it, so the campaign is not clean and broader task status is unchanged. Private-loader follow-up (2026-10-19): The scoped undefined-rejection mechanism is now reproduced/fixed with pinned old/current assets and final browser/native regressions; unique attribution of the original missing trace remains unavailable. See the Node PRIVATE-LOADER-JS-20261019 report. Earlier campaign failures and broader partial gates are unchanged.
P095-006 todo 005 Reuse the same mechanics on one existing configuration or Corpus inspection surface before extracting a general library; keep writes with their current owners and document remaining platform limits.
P095-007 partial — Disable snapshots; purge known prior history/cache keys; add no-store/Vary and desktop propagation; cover all asset includes with integrity. Gate: pre-seeded old snapshots, bfcache, offline Back and other windows after identity change cannot restore prior content; actual browser/WebView limits are recorded. 2026-10-18: All owning layouts disable snapshots/eval/script tags, purge only owned prior keys and carry SRI/private headers. Real browser restoration/offline/other-window and native identity-change/Back/unavailable-owner checks pass; later trusted actual BFCache admissions remove private markers in Chrome configuration and native HTTP/custom top documents with scripts enabled. Operational no-JS DOM retention is not secret classification. Sensitive shells and three actual trusted seed-view restores pass on final unchanged UI/Desktop/owner binaries after normal navigation, context change and owner stop; effective JS/nonpersistent readback and real native unlock pass separately. BFCache/memory-cache disablement is not claimed. Broader gates remain partial.
P095-008 partial — Implement parsed fragment/response confinement or explicitly isolate incompatible surfaces; migrate inline scripts/event attributes and eval features; enforce CSP through HTTP and desktop host pages. Inventory IPC commands and enforce caller/argument scope independently of withGlobalTauri. Gate: script, same-origin src, declarative target/action/OOB and header injections cannot cross module/shell/native boundaries; framing and legitimate settings still work. 2026-10-18: Parsed passive module admission, inline migration, self/nonce CSP and actual native caller/argument refusals plus legitimate Settings are implemented and reviewed. Executable modules require separate reviewed isolation; broader platform qualification remains open.
P095-009 partial 001 Decide where the conversation lives: move it from #orb-modal-slot into #app-main, or record why the first slice uses another pair. Gate: opening details never stacks a panel inside a modal, and closing a modal restores focus to its opener. 2026-10-18: The accepted first pair is component/configuration, not conversation. Stable separate modal/panel slots, local modal close and independent focus/Escape are implemented; conversation remains in its current owner without broader migration claims.
P095-010 partial 001, 009 Implement panel representation negotiation, authorized full-page fallbacks, validated href metadata, Vary/no-store including desktop transport, and confined response effects. Gate: direct/full/fragment requests, spoofed HX headers, unwanted response headers and OOB are tested; shell IDs remain unique after navigation. 2026-10-18: Exact dual-header negotiation, authorized full pages, closed href metadata, private headers and early/final response gates pass for the first pair. Other resource pairs/queries are not automatically admitted.
P095-011 partial 002 Extract pure stack/epoch/generation, tail and draft predicates. Gate: bounds, drop-oldest, failed navigation, recreated-controller stale reads, safe counter exhaustion, message-count saturation, and typing after discard consent are tested without a browser. 2026-10-18: Pure eight-entry href/opener stack, unique epoch and non-wrapping generations have focused tests and browser bounds/refetch proof. Tail/draft predicates belong to the deferred conversation slice.

Closeout qualifier for P095-004/005/007/010 — 2026-10-18: actual BFCache observations supersede the earlier non-observation only for their pinned engines/transports. A current-build scripted full-page/panel walkthrough is not the missing historical ergonomic baseline. Fully disabled page JavaScript cannot execute the restoration guard. The later accepted sensitive-view amendment therefore prevents sensitive HTML from entering such a document at all, instead of claiming that operational no-JS DOM cannot be cached. It also supersedes the earlier unexplained-bootstrap implementation gap with a retained controlled counterexample and bounded fix, not retrospective attribution of every timeout. These broader rows remain partial; consult the restoration/bootstrap follow-up for current evidence, cache axes, historical refusal limits and platform scope.

Final review amendment — accepted 2026-10-18: configuration participant readiness is a separate authorized no-store JS fragment. Initial full-page/panel HTML has only a fixed placeholder and no participant id/status/reasons; ordinary configuration navigation/forms remain no-JS and do not wait for readiness. The UI never replaces fields in the configuration owner's inspection DTO. Locked-key guidance uses participant-key-locked without private model data. The scoped configuration no-JS privacy gate is therefore closed by construction, not by browser BFCache policy. Native iframe provisional/commit/failure codes remain unavailable through the approved public boundary; no SPI is authorized. Frequency samples are independent current measurements, not historical causes. The lost-code historical CLI refusal is closed as unattributable; future CLI failures have closed machine codes retained by the harness. The pre-change ergonomic baseline is unavailable-closed; a frozen current baseline/v1 is for future comparison only. These dispositions close bookkeeping, not broader task, human-usability, native instrumentation or release gates. See node:docs/evidence/configuration-service-control/REVIEW-CLOSEOUT-20261018.md.

S020 reliability follow-up — 2026-10-18: the configuration consumer's lost disable after remove/start/false between equal false observations is reproduced through normal local HTTP and corrected against its application fence. This is not a UI retry, a longer timeout, a changed enable/stop contract or proof of the snapshot-less historical attempt's exact cause. D1 regression and the differential control are retained in node:docs/evidence/configuration-service-control/S020-TIMEOUT-20261018.md; whole P095/platform/release status remains unchanged.

Open Questions

  • Which existing conversation/detail pair avoids introducing new domain endpoints? P095-001 owns this selection; a mocked backend must be labelled as such.
  • Does the pinned Stimulus integration simplify maintenance in practice? P095-002 decides; do not substitute a broad framework migration on failure.
  • Should the conversation leave the modal slot for the main surface (P095-009), and does that change the operator console as well as /app?
  • Can the desktop host drop the global convenience API without breaking existing native integration, and which command/window/frame permissions are required? P095-008 must resolve actual IPC authority separately from that flag.

Next Actions

Land P095-007 first: it removes an existing history exposure and does not depend on the framework decision. Start P095-001 in parallel, then the compatibility spike. Do not add docking, persistent draft storage or arbitrary native windows until the first slice passes its gates.

Agora inline-unlock acknowledgement follow-up (2026-10-19)

P095-004 retains an unqualified integration boundary: the initial P035 full collector observed HTTP 423 from the presentation-context fence after the Agora compose handler unlocked a protected participant key during submission. The context fence must continue rejecting stale/foreign context; investigate a trusted owner acknowledgement for this explicit transition without granting module-controlled authority. Determine whether the publication already happened before its acknowledgement was discarded, and pin retry/idempotency behavior. The original incomplete P035 collector remains private retained evidence.

P035's three UI history-authoring samples explicitly unlock through the host before preview. Their eventual transport/recovery qualification does not close this inline-unlock acknowledgement gate. The owning implementation contract is node:node-ui/src/presentation_context.rs; Node's agora-relay-matrix/docs/ROOM-RECOVERY.md records the observed boundary and scope.