Proposal 095: Node UI Interaction Layer¶
Based on:
- Challenge 095: UI Workspace Continuity
- Core Values
- HTMX/HATEOAS architecture memo
- P052: Tauri-Hosted Node UI
- P091: File-backed Configuration
- Solution 001: Node UI
- Solution 019: Middleware
node:DEV-GUIDELINES.md(Layering; Data and Contracts; thin UI boundaries)
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-targetsand 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) andorbiplex-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, andorbiplex-user.jsopens it through its ownfetch, 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
/appshell navigate withhx-boostandhx-push-url. Nohx-historyattribute orhtmx.configoverride exists. The vendored 2.0.8 implementation usessessionStorageforhtmx-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 noscript-src. The desktop host configures Tauri withcsp: nullandwithGlobalTauri: 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 throughHtml(...)andfragment | safe;normalize_server_html_fragmentrewrites 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_HEADERSomits CSP,Cache-ControlandVary; server-side header changes alone do not reach the custom-origin document. The bootstrap embeds/appin an iframe, so blindly forwardingframe-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:afterSwapis 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¶
- 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. - Keep controllers with
node-ui/static/and contracts/examples innode:node-ui/README.md(longer material in itsdocs/). Use explicit host-owned registration, small controllers and declarative HTML bindings. - 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.
- 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.
- 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.
- Keep runnable acceptance beside
node:tools/acceptance/harnesses and results undernode: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-mainkeeps the existinghx-select/hx-push-urlnavigation 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-panelis non-modal: no focus trap, noaria-modal, Escape closes it only when focus is inside it, and the main surface stays operable.#orb-modal-slotstays the one modal owner for confirmations and short forms. A detail panel never opens inside a modal; a modal may open above a panel.
Panel link and fragment contract¶
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:beforeRequestadmits the panel read, incrementsgenerationand captures{epoch, generation, href}plus the opener in a localWeakMapkeyed 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 exposerequestConfig.headerson 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()athtmx:beforeOnLoad, before response headers cause effects.beforeSwapis too late forHX-Redirect/HX-Triggerand 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.
DraftStatedrives discard confirmation before replacing that composer, usinghtmx:confirm. Bind consent to its revision and recheck before destructive swap, so typing while navigation is pending cannot silently lose new text. Attachbeforeunloadonly 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.pinnedis true while the history scroller is within 32 px of its end, measured before content growth. When new messages arrive unpinned, the controller incrementsunseen(saturating at 65535, displayed as65535+), 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-requiredinvalidate 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 knownhtmx-history-cachekey from currentsessionStorageand legacylocalStorageat upgrade and identity invalidation, together with the ownedhtmx-current-path-for-historymetadata; never clear unrelated origin storage. Missing-history navigation reloads under current authorization. Per-surfacehx-history="false"is an extra guard. - Treat HTMX snapshots, HTTP cache and browser back/forward cache as different
mechanisms.
no-storeand the HTMX options are not a universal bfcache ban. Coverpageshowrestoration, 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 enableunsafe-eval/unsafe-inlineto silence breakage. Pin an explicit policy forallowEvalandallowScriptTagsafter 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,
/appiframe and settings documents without accidentally breaking framing or allowing arbitrary embedding. - Decide
withGlobalTauriindependently of IPC authorization: removingwindow.__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.cjsdemonstrates 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-driversupports 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.