diff --git a/TREE.md b/TREE.md index 8cbd391..00106df 100644 --- a/TREE.md +++ b/TREE.md @@ -353,6 +353,8 @@ research/evolution/context.json research/evolution/cycles/2026-09-27-navigation-effort.json research/evolution/cycles/2026-09-27-navigation-focus-effort.json research/evolution/cycles/2026-09-27-navigation-focus.md +research/evolution/cycles/2026-09-27-navigation-ownership-effort.json +research/evolution/cycles/2026-09-27-navigation-ownership.md research/evolution/cycles/2026-09-27-navigation.md research/evolution/ledger.json research/evolution/profiles.json @@ -407,6 +409,11 @@ research/experiments/navigation-history/focus-policy.js research/experiments/navigation-history/focus-result.json research/experiments/navigation-history/focus.mjs research/experiments/navigation-history/initial-partial.json +research/experiments/navigation-history/ownership-initial-result.json +research/experiments/navigation-history/ownership-initial.mjs +research/experiments/navigation-history/ownership-policy.js +research/experiments/navigation-history/ownership-result.json +research/experiments/navigation-history/ownership.mjs research/experiments/navigation-history/package-lock.json research/experiments/navigation-history/package.json research/experiments/navigation-history/result.json @@ -431,7 +438,9 @@ research/reviews/MatrixRecoveryExperiment.md research/reviews/NavigationCoverage.md research/reviews/NavigationFocusExperiment.md research/reviews/NavigationHistoryExperiment.md +research/reviews/NavigationOwnershipExperiment.md research/reviews/NavigationRenderingEvidence.md +research/reviews/NavigationThreadReview.md research/reviews/NestedDismissalIsolation.md research/reviews/NestedOwnershipRouting.md research/reviews/ObsidianConflictScreen.json diff --git a/research/candidates/FocusReturn.md b/research/candidates/FocusReturn.md index 875de54..dcfe1f0 100644 --- a/research/candidates/FocusReturn.md +++ b/research/candidates/FocusReturn.md @@ -85,3 +85,12 @@ this fixture restores a same-ID link; that behavior alone does not choose the ne task. Preserve this candidate's modal scope instead of treating every route change as a return-to-invoker problem. No general route-focus pattern or AT validation is admitted; delayed history restoration and competing navigation remain open. + +## Completion ownership contrast — 2026-09-27 + +[NO01–NO06](../reviews/NavigationOwnershipExperiment.md) strengthen the route/modal +scope boundary. A still-current history result can restore content without taking +focus from a newer search task; an obsolete navigation result must not supply a +continuation at all. The earlier single-response route policy is insufficient for +competing completions. This contrast does not expand the modal candidate into a +universal routing pattern or prove AT behavior. diff --git a/research/candidates/ProgressiveEnhancement.md b/research/candidates/ProgressiveEnhancement.md index 54dc834..66be540 100644 --- a/research/candidates/ProgressiveEnhancement.md +++ b/research/candidates/ProgressiveEnhancement.md @@ -65,3 +65,18 @@ return to the invoker. Check ownership when the response is applied, not only wh it is requested. Failure feedback need not move focus. Same-ID restoration is not proof of semantic continuity. DOM/ARIA checks here do not establish spoken feedback; AT, delayed history and overlapping-request behavior remain unverified. + +## Separate completion and focus ownership — 2026-09-27 + +[Competing navigation](../reviews/NavigationOwnershipExperiment.md) NO01–NO06 exposes +limits of the preceding single-response policy. Validate ownership before any result +effects, including old errors and history restoration, not only before moving focus. +A current response may update content/title/status while the user has moved focus +to another surviving task; an obsolete response may own none of those effects. +The latest-intent policy tested here does not revive older success when newer intent +fails. Ignoring completion does not abort or undo server work. + +A scoped navigation generation and separate focus-change check meet the tested +obligations. Their history adapter depends on pinned htmx internals; multiple history +misses, nonzero swap delays, redirects, lifecycle teardown and AT validation remain +open. Do not apply this navigation policy indiscriminately to unrelated background work. diff --git a/research/candidates/URLAddressableState.md b/research/candidates/URLAddressableState.md index 1932fed..4b98984 100644 --- a/research/candidates/URLAddressableState.md +++ b/research/candidates/URLAddressableState.md @@ -47,3 +47,19 @@ a fragment served as a full document in Chromium and Firefox; correct selection keeps representations distinct, without promising cache retention or freshness. See [NH01–NH06](../reviews/NavigationHistoryExperiment.md). One authored application with a real package, not independent deployed recurrence. Hypothesis retained. + +## Competing completion ownership — 2026-09-27 + +[NO01–NO06](../reviews/NavigationOwnershipExperiment.md) show a delayed history response +replacing content/title while a newer URL remains selected in the unguarded fixture. +Treat navigation initiation, including Back/Forward, as intent with an explicit +completion owner. Before applying an asynchronous result, check that it still owns +the selected route; include URL, content, title and feedback in that decision. +A response to obsolete intent is not made current by arriving last or succeeding. + +The experiment's latest-intent policy keeps the last committed view and newest error +when a newer request fails; it does not revive older pending navigation implicitly. +Other policies require an explicit contract. Browser history can change the URL before +loading completes: pending-state disclosure/recovery is still unresolved here. +The authored guard supports this obligation only in the tested schedules, not a +portable router or proof of independent application recurrence. diff --git a/research/catalog/SourceCatalog.md b/research/catalog/SourceCatalog.md index fca224d..8d30290 100644 --- a/research/catalog/SourceCatalog.md +++ b/research/catalog/SourceCatalog.md @@ -189,3 +189,10 @@ order and programmatically exposed feedback; no conformance claim from this fixt SRC-0040: [htmx events](https://htmx.org/events/), lifecycle-hook reference accessed 2026-09-27, living revision unknown. Same htmx family as SRC-0018/0019 and SRC-0038. Runtime conclusions use pinned 2.0.11 code and hashed receipts. + +SRC-0038 ownership follow-up, 2026-09-27: [NO01–NO06](../reviews/NavigationOwnershipExperiment.md) +compares the unchanged prior focus policy with an authored generation/focus guard. +24 completed rows, guard 12/12 full obligations versus baseline 0/12, with initial +keyboard-helper failures retained. Same fixture family and pinned htmx package. +SRC-0040 event documentation and pinned loadHistoryFromServer source delimit the +experimental history-XHR adapter; no vendor patch or production claim. diff --git a/research/evolution/EvolutionReport.md b/research/evolution/EvolutionReport.md index d301334..eb7d2e1 100644 --- a/research/evolution/EvolutionReport.md +++ b/research/evolution/EvolutionReport.md @@ -55,9 +55,9 @@ Ten selected retrospective outcomes (not a historical census), followed by prosp | inconclusive | 0 | | merge | 0 | | rejection | 0 | -| revision | 10 | +| revision | 11 | -Cycles with recorded research effort: 2/12. Unknown effort is neither zero nor estimated from commits, tokens or assertion counts. +Cycles with recorded research effort: 3/13. Unknown effort is neither zero nor estimated from commits, tokens or assertion counts. ### Stream B / B-navigation-mechanism-v1 @@ -75,6 +75,14 @@ One bounded question counts at most one consequential outcome. Corroboration and One bounded question counts at most one consequential outcome. Corroboration and inconclusive work remain visible separately. Fewer than three comparable cycles: no trend assessment. +### Stream F / F-navigation-ownership-v1 + +| Cycle | Outcome | Research minutes | Consequential outcome/hour | +| --- | --- | ---: | ---: | +| LEARN-0013 | revision | 13.918 | 4.31 | + +One bounded question counts at most one consequential outcome. Corroboration and inconclusive work remain visible separately. Fewer than three comparable cycles: no trend assessment. + ## Coherence and maintainability Profile hashes and local references passed validation when this report was generated. That checks traceability, not source truth or external URL freshness. @@ -222,8 +230,9 @@ Make a navigable state reconstructible from its URL. | documentary | htmx | History behavior and history-cache misses. | [NavigationRenderingEvidence](../../research/reviews/NavigationRenderingEvidence.md) | | documentary | Web platform | pushState changes history, not server dereferenceability. | [NavigationRenderingEvidence](../../research/reviews/NavigationRenderingEvidence.md) | | runtime | htmx in authored public-catalog fixture | NH01–NH06: native/enhanced routes, history hit/miss, actual browser cache ordering and missing-Vary negative control; explicit Node validator checks separate. | [NavigationHistoryExperiment](../../research/reviews/NavigationHistoryExperiment.md) | +| runtime | htmx in authored public-catalog fixture | NO01–NO06: ordered history/success/error races, prior policy 0/12 full obligations versus guarded 12/12; separate focus ownership. Same fixture family; pinned history-XHR adapter. | [NavigationOwnershipExperiment](../../research/reviews/NavigationOwnershipExperiment.md) | -Open limits: Independent deployed full-contract recurrence remains unverified. Keyboard/focus/announcements, late-script activation, interrupted transport, invalid/deleted routes, offline availability and real intermediary behavior remain open. +Open limits: Independent application recurrence and human/AT validation remain unverified. Pending history disclosure, multiple history misses, redirects, nonzero swap delays, bfcache, lifecycle cleanup, interrupted/late enhancement and offline availability remain open. Superseded by: none recorded. @@ -239,8 +248,9 @@ Keep essential actions usable when enhancement is unavailable. | documentary | SvelteKit | Form actions and rendering/page options. | [NavigationRenderingEvidence](../../research/reviews/NavigationRenderingEvidence.md) | | runtime | htmx in authored public-catalog fixture | NH01–NH06: native/enhanced routes, history hit/miss, actual browser cache ordering and missing-Vary negative control; explicit Node validator checks separate. | [NavigationHistoryExperiment](../../research/reviews/NavigationHistoryExperiment.md) | | runtime | htmx in authored public-catalog fixture | NF01–NF06: Tab/Enter navigation, raw versus authored focus/status policy, held-response ownership and background update; DOM evidence only, not AT speech. | [NavigationFocusExperiment](../../research/reviews/NavigationFocusExperiment.md) | +| runtime | htmx in authored public-catalog fixture | NO01–NO06: ordered history/success/error races, prior policy 0/12 full obligations versus guarded 12/12; separate focus ownership. Same fixture family; pinned history-XHR adapter. | [NavigationOwnershipExperiment](../../research/reviews/NavigationOwnershipExperiment.md) | -Open limits: Independent deployed full-contract recurrence and human/AT validation remain unverified. Focus visibility, delayed history, overlapping requests, late-script activation, interrupted transport, invalid/deleted routes, offline availability and real intermediaries remain open. +Open limits: Independent application recurrence and human/AT validation remain unverified. Pending history disclosure, multiple history misses, redirects, nonzero swap delays, bfcache, lifecycle cleanup, interrupted/late enhancement and offline availability remain open. Superseded by: none recorded. @@ -328,8 +338,9 @@ Restore meaningful focus after a temporary interaction ends. | static-code | Radix | Pinned layer/listener ownership implementation. | [NestedDismissalIsolation](../../research/reviews/NestedDismissalIsolation.md) | | runtime | Radix | Authored fixture: raw 20/24 versus routed 24/24; browser/build limits retained. | [NestedOwnershipRouting](../../research/reviews/NestedOwnershipRouting.md) | | runtime | htmx in authored public-catalog fixture | NF01–NF06: Tab/Enter navigation, raw versus authored focus/status policy, held-response ownership and background update; DOM evidence only, not AT speech. | [NavigationFocusExperiment](../../research/reviews/NavigationFocusExperiment.md) | +| runtime | htmx in authored public-catalog fixture | NO01–NO06: ordered history/success/error races, prior policy 0/12 full obligations versus guarded 12/12; separate focus ownership. Same fixture family; pinned history-XHR adapter. | [NavigationOwnershipExperiment](../../research/reviews/NavigationOwnershipExperiment.md) | -Open limits: One implementation family; repeated trials are not independent uses. Animated exits and competing owners remain untested; no production repair claim. Route continuation is a contrasting context, not independent modal-pattern recurrence; delayed history and AT speech unverified. +Open limits: One implementation family; repeated trials are not independent uses. Animated exits and competing owners remain untested; no production repair claim. Route ownership is a contrast, not independent modal recurrence; AT speech and broader history/lifecycle cases remain unverified. Superseded by: none recorded. @@ -519,3 +530,17 @@ Evidence: [NavigationFocusExperiment](../../research/reviews/NavigationFocusExpe Remaining uncertainty: Author-model DOM evidence, no human/AT validation or independent recurrence. Delayed history focus, competing requests, delayed scripts and interrupted enhancement remain open. Supersedes: none recorded. + +### LEARN-0013 — Can obsolete history/navigation completions affect newer intent, and does separating completion ownership from focus ownership prevent it? + +2026-09-27 · stream F · prospective · revision + +Before: The single-response continuation policy preserved newer search focus but left competing history/success/error completions unresolved; focus-at-swap alone might not be sufficient. + +Conclusion: Revise operational guidance: validate navigation ownership before content/URL/title/feedback effects, including stale errors and newer failure, independently of focus permission. Prior policy misses obligations in 12/12 adversarial rows; authored guard meets 12/12. Pause fixture work for independent source contrast. + +Evidence: [NavigationOwnershipExperiment](../../research/reviews/NavigationOwnershipExperiment.md), [NavigationThreadReview](../../research/reviews/NavigationThreadReview.md), [ownership-result](../../research/experiments/navigation-history/ownership-result.json), [2026-09-27-navigation-ownership-effort](../../research/evolution/cycles/2026-09-27-navigation-ownership-effort.json). + +Remaining uncertainty: One authored htmx family and author-model review, not production recurrence or AT validation. History adapter is pinned implementation-dependent; pending-state, multiple-miss and lifecycle limits remain open. Three distinct protocols are not a comparable diminishing-returns window. + +Supersedes: none recorded. diff --git a/research/evolution/cycles/2026-09-27-navigation-ownership-effort.json b/research/evolution/cycles/2026-09-27-navigation-ownership-effort.json new file mode 100644 index 0000000..fdea88f --- /dev/null +++ b/research/evolution/cycles/2026-09-27-navigation-ownership-effort.json @@ -0,0 +1,42 @@ +{ + "start": 199629.983211976, + "execution_seconds": 74.26050614900305, + "executions": [ + { + "command": [ + "node", + "research/experiments/navigation-history/ownership.mjs" + ], + "seconds": 23.363541921018623, + "exit_code": 1 + }, + { + "command": [ + "node", + "research/experiments/navigation-history/ownership.mjs" + ], + "seconds": 37.19569087898708, + "exit_code": 0 + }, + { + "command": [ + "make", + "evolution-check", + "evolution-test", + "schema-test", + "bench-test" + ], + "seconds": 13.701273348997347, + "exit_code": 0 + } + ], + "status": "complete", + "end_utc": "2026-09-26T22:45:32.069777+00:00", + "start_utc_approx": "2026-09-26T22:30:22.733700+00:00", + "elapsed_seconds": 909.336, + "research_minutes": 13.918, + "execution_minutes": 1.238, + "method": "Monotonic single-agent session minus timed experiment/check subprocess intervals. Initial harness failures and corrected run both retained in execution. Checkpoint inspection assigned to execution, not double-counted.", + "excluded": "Orientation before start and final receipt refresh, commits, snapshot, decision/progress logging and sync after cutoff; unknown model tokens/cost.", + "budget": "Within registered 45 active / 10 execution minute budget; no scope extension." +} diff --git a/research/evolution/ledger.json b/research/evolution/ledger.json index f4e41a4..4078cc0 100644 --- a/research/evolution/ledger.json +++ b/research/evolution/ledger.json @@ -410,6 +410,46 @@ "cost_usd": null }, "supersedes": [] + }, + { + "id": "LEARN-0013", + "date": "2026-09-27", + "recording": "prospective", + "stream": "F", + "comparison_group": "F-navigation-ownership-v1", + "protocol_ref": "research/evolution/cycles/2026-09-27-navigation-ownership.md", + "question": "Can obsolete history/navigation completions affect newer intent, and does separating completion ownership from focus ownership prevent it?", + "previous_belief": "The single-response continuation policy preserved newer search focus but left competing history/success/error completions unresolved; focus-at-swap alone might not be sufficient.", + "conclusion": "Revise operational guidance: validate navigation ownership before content/URL/title/feedback effects, including stale errors and newer failure, independently of focus permission. Prior policy misses obligations in 12/12 adversarial rows; authored guard meets 12/12. Pause fixture work for independent source contrast.", + "disposition": "revision", + "affected_patterns": [ + "CAND-0009", + "CAND-0010", + "CAND-0016" + ], + "evidence": [ + "research/reviews/NavigationOwnershipExperiment.md", + "research/reviews/NavigationThreadReview.md", + "research/experiments/navigation-history/ownership-result.json", + "research/evolution/cycles/2026-09-27-navigation-ownership-effort.json" + ], + "remaining_uncertainty": [ + "One authored htmx family and author-model review, not production recurrence or AT validation.", + "History adapter is pinned implementation-dependent; pending-state, multiple-miss and lifecycle limits remain open.", + "Three distinct protocols are not a comparable diminishing-returns window." + ], + "reviewer": { + "name": "Codex", + "role": "author-model", + "independent_human": false + }, + "effort": { + "research_minutes": 13.918, + "execution_minutes": 1.238, + "reported_tokens": null, + "cost_usd": null + }, + "supersedes": [] } ] } diff --git a/research/evolution/profiles.json b/research/evolution/profiles.json index 7dcf782..ac0586a 100644 --- a/research/evolution/profiles.json +++ b/research/evolution/profiles.json @@ -317,7 +317,7 @@ "id": "CAND-0009", "path": "research/candidates/URLAddressableState.md", "title": "URL-addressable State", - "sha256": "3edc087c5b4b0a332dd9097334022cdb138437413b933f271e5cf96096cc63be", + "sha256": "7374180994e13cbca7b1dd4cf4da27c36926e462e844bbc0beb42e5ef6e562f5", "status": "draft", "maturity": "hypothesis", "decision": "Make a navigable state reconstructible from its URL.", @@ -339,6 +339,12 @@ "family": "htmx in authored public-catalog fixture", "ref": "research/reviews/NavigationHistoryExperiment.md", "scope": "NH01–NH06: native/enhanced routes, history hit/miss, actual browser cache ordering and missing-Vary negative control; explicit Node validator checks separate." + }, + { + "kind": "runtime", + "family": "htmx in authored public-catalog fixture", + "ref": "research/reviews/NavigationOwnershipExperiment.md", + "scope": "NO01–NO06: ordered history/success/error races, prior policy 0/12 full obligations versus guarded 12/12; separate focus ownership. Same fixture family; pinned history-XHR adapter." } ], "independent_full_contract_uses": null, @@ -350,8 +356,8 @@ "last_substantive_review_date": "2026-09-27" }, "gaps": [ - "Independent deployed full-contract recurrence remains unverified.", - "Keyboard/focus/announcements, late-script activation, interrupted transport, invalid/deleted routes, offline availability and real intermediary behavior remain open." + "Independent application recurrence and human/AT validation remain unverified.", + "Pending history disclosure, multiple history misses, redirects, nonzero swap delays, bfcache, lifecycle cleanup, interrupted/late enhancement and offline availability remain open." ], "superseded_by": [] }, @@ -359,7 +365,7 @@ "id": "CAND-0010", "path": "research/candidates/ProgressiveEnhancement.md", "title": "Progressive Enhancement", - "sha256": "ec3ab16d680308593af4d22fbbcbea737fe46096c5f3e65968d0db3a8befec21", + "sha256": "3eebc18862e1f5cd0221fa72f468a74719e7fbeab407cc81930d9e4ea397b3b5", "status": "draft", "maturity": "hypothesis", "decision": "Keep essential actions usable when enhancement is unavailable.", @@ -387,6 +393,12 @@ "family": "htmx in authored public-catalog fixture", "ref": "research/reviews/NavigationFocusExperiment.md", "scope": "NF01–NF06: Tab/Enter navigation, raw versus authored focus/status policy, held-response ownership and background update; DOM evidence only, not AT speech." + }, + { + "kind": "runtime", + "family": "htmx in authored public-catalog fixture", + "ref": "research/reviews/NavigationOwnershipExperiment.md", + "scope": "NO01–NO06: ordered history/success/error races, prior policy 0/12 full obligations versus guarded 12/12; separate focus ownership. Same fixture family; pinned history-XHR adapter." } ], "independent_full_contract_uses": null, @@ -398,8 +410,8 @@ "last_substantive_review_date": "2026-09-27" }, "gaps": [ - "Independent deployed full-contract recurrence and human/AT validation remain unverified.", - "Focus visibility, delayed history, overlapping requests, late-script activation, interrupted transport, invalid/deleted routes, offline availability and real intermediaries remain open." + "Independent application recurrence and human/AT validation remain unverified.", + "Pending history disclosure, multiple history misses, redirects, nonzero swap delays, bfcache, lifecycle cleanup, interrupted/late enhancement and offline availability remain open." ], "superseded_by": [] }, @@ -566,7 +578,7 @@ "id": "CAND-0016", "path": "research/candidates/FocusReturn.md", "title": "Focus Return", - "sha256": "129ef7623d11da91f3f5429686455be16180e5477d1706836f5c1c3617698eae", + "sha256": "a09b79e3b481caab81a7f9ee10d895ba77c6c7eedb71f4313733735eea58a477", "status": "draft", "maturity": "hypothesis", "decision": "Restore meaningful focus after a temporary interaction ends.", @@ -594,6 +606,12 @@ "family": "htmx in authored public-catalog fixture", "ref": "research/reviews/NavigationFocusExperiment.md", "scope": "NF01–NF06: Tab/Enter navigation, raw versus authored focus/status policy, held-response ownership and background update; DOM evidence only, not AT speech." + }, + { + "kind": "runtime", + "family": "htmx in authored public-catalog fixture", + "ref": "research/reviews/NavigationOwnershipExperiment.md", + "scope": "NO01–NO06: ordered history/success/error races, prior policy 0/12 full obligations versus guarded 12/12; separate focus ownership. Same fixture family; pinned history-XHR adapter." } ], "independent_full_contract_uses": null, @@ -607,7 +625,7 @@ "gaps": [ "One implementation family; repeated trials are not independent uses.", "Animated exits and competing owners remain untested; no production repair claim.", - "Route continuation is a contrasting context, not independent modal-pattern recurrence; delayed history and AT speech unverified." + "Route ownership is a contrast, not independent modal recurrence; AT speech and broader history/lifecycle cases remain unverified." ], "superseded_by": [] }, diff --git a/research/experiments/navigation-history/README.md b/research/experiments/navigation-history/README.md index ecd828d..86aaca7 100644 --- a/research/experiments/navigation-history/README.md +++ b/research/experiments/navigation-history/README.md @@ -53,3 +53,24 @@ Raw rows observe behavior, so 22 passing rows do not mean raw and managed varian meet identical focus/feedback obligations. See NF01–NF06. The policy is authored experimental code; DOM focus and live-region text do not establish assistive-technology announcements or conformance. History and request-race limits remain explicit. + +## Competing completion follow-up and pause + +[LEARN-0013 protocol](../../evolution/cycles/2026-09-27-navigation-ownership.md), +[review](../../reviews/NavigationOwnershipExperiment.md), [receipt](ownership-result.json). +Use the same browser environment with `node ownership.mjs`; choose a new +`FEP_RESULT_PATH` (default `/tmp/fep-ownership-result.json`). Six scenarios × two +policies × two browsers: 24 completed rows, all obligations met by guard 12/12 and +previous policy 0/12. These are deliberately adversarial cases, not general quality +scores. Server barriers impose schedules; client completions are observed without +aborting transport or claiming remote rollback. + +The previous focus-policy.js remains byte-for-byte unchanged. New ownership-policy.js +uses a generation plus separate focus ownership; its history XHR handler wrapping is +explicitly tied to pinned internals. Initial runner/result retained under +ownership-initial.mjs / ownership-initial-result.json: four Firefox keyboard-helper +failures corrected by backward traversal, without changing policies or obligations. +Initial receipt's ownership.mjs hash corresponds to ownership-initial.mjs. + +The [thread review](../../reviews/NavigationThreadReview.md) pauses fixture expansion +and hands the next evidence question to an independent pinned application source study. diff --git a/research/experiments/navigation-history/ownership-initial-result.json b/research/experiments/navigation-history/ownership-initial-result.json new file mode 100644 index 0000000..847481b --- /dev/null +++ b/research/experiments/navigation-history/ownership-initial-result.json @@ -0,0 +1,2088 @@ +{ + "date": "2026-09-26T22:33:49.800Z", + "node": "v24.11.1", + "kind": "ordered-navigation-completion-races-in-authored-htmx-fixture", + "files": { + "ownership.mjs": "4fcd57e451231de9e6f8017f4ed16e34e602f0750f26b80beeac365c43cdba8f", + "ownership-policy.js": "154cd4aafe4c77ba826a1bc2e6eb0d728e4a9169f23dbfe8ee3c377744273dd9", + "focus-policy.js": "cb91524d66cfff97b0bed905502479ab3134eecdfb6964a3b226d516476b145a", + "package.json": "373e05f4d2b9e043abc9391e6ec28bcf6ffdd5530e52f3e5db88ed20e4462ed5", + "package-lock.json": "6fb5a45148a199c10fd9b67b1d5468a0334e5fcc154f1f079fd88462a89328d3", + "node_modules/htmx.org/dist/htmx.js": "30612ca7e3d3baf407481f1798281c4481dcb93390a9722d328ce07290250426" + }, + "browsers": { + "chromium": { + "version": "151.0.7922.34", + "executable": "/home/worsch/.cache/ms-playwright/chromium-1234/chrome-linux64/chrome", + "sha256": "0b20b130e7edd9dd51873be867761295fe0cfad490c2b9a64f95bd3cfc08fa71" + }, + "firefox": { + "version": "155.0", + "executable": "/tmp/fep-playwright-browsers/firefox-1543/firefox/firefox", + "sha256": "062272ac4d851eade5b75db2dc7c14569d96cb3ca38f949337cdc1b74367e35b" + } + }, + "results": [ + { + "id": "O01-history-focus-moved", + "engine": "chromium", + "mode": "managed", + "status": "pass", + "before": { + "topic": "beta", + "url": "/articles?topic=alpha&mode=managed", + "title": "beta — Catalog", + "focus": "search", + "search": "new task", + "feedback": "Loaded beta" + }, + "after": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=managed", + "title": "alpha — Catalog", + "focus": "heading", + "search": "new task", + "feedback": "Loaded alpha" + }, + "obligations": { + "selection": true, + "title": true, + "focus": false, + "typedText": true + }, + "obligationsMet": false, + "final": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=managed", + "title": "alpha — Catalog", + "focus": "heading", + "search": "new task", + "feedback": "Loaded alpha" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=managed", + "feedback": "Loaded beta" + }, + { + "name": "htmx:afterSettle", + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=alpha&mode=managed", + "feedback": "Loaded beta" + }, + { + "name": "htmx:historyRestore", + "focus": "heading", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=alpha&mode=managed", + "feedback": "Loaded alpha" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=managed", + "status": 200 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=alpha&mode=managed", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=managed", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": true, + "status": 200, + "held": true, + "released": true + } + ] + }, + { + "id": "O02-history-superseded", + "engine": "chromium", + "mode": "managed", + "status": "pass", + "before": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=managed", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "after": { + "topic": "alpha", + "url": "/articles?topic=gamma&mode=managed", + "title": "alpha — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded alpha" + }, + "obligations": { + "newerSelection": false, + "noFeedbackOrFocusTheft": false + }, + "obligationsMet": false, + "final": { + "topic": "alpha", + "url": "/articles?topic=gamma&mode=managed", + "title": "alpha — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded alpha" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=managed", + "feedback": "Loaded beta" + }, + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "gamma — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=gamma&mode=managed", + "feedback": "Loaded gamma" + }, + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=gamma&mode=managed", + "feedback": "Loaded gamma" + }, + { + "name": "htmx:historyRestore", + "focus": "heading", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=gamma&mode=managed", + "feedback": "Loaded alpha" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=managed", + "status": 200 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=gamma&mode=managed", + "status": 200 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=alpha&mode=managed", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=managed", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": true, + "status": 200, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=managed", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + } + ] + }, + { + "id": "O03-old-success", + "engine": "chromium", + "mode": "managed", + "status": "pass", + "before": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=managed", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "after": { + "topic": "beta", + "url": "/articles?topic=beta&mode=managed&hold=old", + "title": "beta — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded beta" + }, + "obligations": { + "newerSelection": false, + "noFeedbackOrFocusTheft": false + }, + "obligationsMet": false, + "final": { + "topic": "beta", + "url": "/articles?topic=beta&mode=managed&hold=old", + "title": "beta — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded beta" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "gamma — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=gamma&mode=managed", + "feedback": "Loaded gamma" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=gamma&mode=managed", + "status": 200 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=managed&hold=old", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=managed&hold=old", + "hx": true, + "history": false, + "status": 200, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=managed", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + } + ] + }, + { + "id": "O04-old-error", + "engine": "chromium", + "mode": "managed", + "status": "pass", + "before": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=managed", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "after": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=managed", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + }, + "obligations": { + "newerSelection": true, + "noObsoleteFeedback": false + }, + "obligationsMet": false, + "final": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=managed", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "gamma — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=gamma&mode=managed", + "feedback": "Loaded gamma" + }, + { + "name": "htmx:responseError", + "focus": "heading", + "title": "gamma — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=gamma&mode=managed", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=gamma&mode=managed", + "status": 200 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=managed&hold=old&fail=1", + "status": 503 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=managed&hold=old&fail=1", + "hx": true, + "history": false, + "status": 503, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=managed", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + } + ] + }, + { + "id": "O05-newer-fails", + "engine": "chromium", + "mode": "managed", + "status": "pass", + "before": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=managed", + "title": "alpha — Catalog", + "focus": "newfail", + "search": "", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + }, + "after": { + "topic": "beta", + "url": "/articles?topic=beta&mode=managed&hold=old", + "title": "beta — Catalog", + "focus": "newfail", + "search": "", + "feedback": "Loaded beta" + }, + "obligations": { + "committedView": false, + "newestFailureRetained": false + }, + "obligationsMet": false, + "final": { + "topic": "beta", + "url": "/articles?topic=beta&mode=managed&hold=old", + "title": "beta — Catalog", + "focus": "newfail", + "search": "", + "feedback": "Loaded beta" + }, + "events": [ + { + "name": "htmx:responseError", + "focus": "newfail", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=alpha&mode=managed", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=gamma&mode=managed&fail=1", + "status": 503 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=managed&hold=old", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=managed&hold=old", + "hx": true, + "history": false, + "status": 200, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=managed&fail=1", + "hx": true, + "history": false, + "status": 503, + "held": false, + "released": true + } + ] + }, + { + "id": "O06-history-newer-than-request", + "engine": "chromium", + "mode": "managed", + "status": "pass", + "before": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=managed", + "title": "alpha — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded alpha" + }, + "after": { + "topic": "beta", + "url": "/articles?topic=beta&mode=managed&hold=old", + "title": "beta — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded beta" + }, + "obligations": { + "historySelection": false, + "historyContinuation": false + }, + "obligationsMet": false, + "final": { + "topic": "beta", + "url": "/articles?topic=beta&mode=managed&hold=old", + "title": "beta — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded beta" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=managed", + "feedback": "Loaded beta" + }, + { + "name": "htmx:afterSettle", + "focus": "old", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=alpha&mode=managed", + "feedback": "Loaded beta" + }, + { + "name": "htmx:historyRestore", + "focus": "heading", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=alpha&mode=managed", + "feedback": "Loaded alpha" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=managed", + "status": 200 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=managed&hold=old", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=managed", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=managed&hold=old", + "hx": true, + "history": false, + "status": 200, + "held": true, + "released": true + } + ] + }, + { + "id": "O01-history-focus-moved", + "engine": "chromium", + "mode": "guarded", + "status": "pass", + "before": { + "topic": "beta", + "url": "/articles?topic=alpha&mode=guarded", + "title": "beta — Catalog", + "focus": "search", + "search": "new task", + "feedback": "Loaded beta" + }, + "after": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "search", + "search": "new task", + "feedback": "Loaded alpha" + }, + "obligations": { + "selection": true, + "title": true, + "focus": true, + "typedText": true + }, + "obligationsMet": true, + "final": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "search", + "search": "new task", + "feedback": "Loaded alpha" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=guarded", + "feedback": "Loaded beta" + }, + { + "name": "htmx:afterSettle", + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=alpha&mode=guarded", + "feedback": "Loaded beta" + }, + { + "name": "htmx:historyRestore", + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=alpha&mode=guarded", + "feedback": "Loaded alpha" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=guarded", + "status": 200 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=alpha&mode=guarded", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=guarded", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": true, + "status": 200, + "held": true, + "released": true + } + ] + }, + { + "id": "O02-history-superseded", + "engine": "chromium", + "mode": "guarded", + "status": "pass", + "before": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "after": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "obligations": { + "newerSelection": true, + "noFeedbackOrFocusTheft": true + }, + "obligationsMet": true, + "final": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=alpha&mode=guarded", + "feedback": "Loaded beta" + }, + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "gamma — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=gamma&mode=guarded", + "feedback": "Loaded gamma" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=guarded", + "status": 200 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=gamma&mode=guarded", + "status": 200 + }, + { + "event": "discard-history", + "url": "http://127.0.0.1:39063/articles?topic=alpha&mode=guarded" + }, + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=alpha&mode=guarded", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=guarded", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": true, + "status": 200, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=guarded", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + } + ] + }, + { + "id": "O03-old-success", + "engine": "chromium", + "mode": "guarded", + "status": "pass", + "before": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "after": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "obligations": { + "newerSelection": true, + "noFeedbackOrFocusTheft": true + }, + "obligationsMet": true, + "final": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "gamma — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=gamma&mode=guarded", + "feedback": "Loaded gamma" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=gamma&mode=guarded", + "status": 200 + }, + { + "event": "discard-navigation", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=guarded&hold=old" + }, + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=guarded&hold=old", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=guarded&hold=old", + "hx": true, + "history": false, + "status": 200, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=guarded", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + } + ] + }, + { + "id": "O04-old-error", + "engine": "chromium", + "mode": "guarded", + "status": "pass", + "before": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "after": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "obligations": { + "newerSelection": true, + "noObsoleteFeedback": true + }, + "obligationsMet": true, + "final": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "gamma — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=gamma&mode=guarded", + "feedback": "Loaded gamma" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=gamma&mode=guarded", + "status": 200 + }, + { + "event": "discard-navigation", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=guarded&hold=old&fail=1" + }, + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=guarded&hold=old&fail=1", + "status": 503 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=guarded&hold=old&fail=1", + "hx": true, + "history": false, + "status": 503, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=guarded", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + } + ] + }, + { + "id": "O05-newer-fails", + "engine": "chromium", + "mode": "guarded", + "status": "pass", + "before": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "newfail", + "search": "", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + }, + "after": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "newfail", + "search": "", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + }, + "obligations": { + "committedView": true, + "newestFailureRetained": true + }, + "obligationsMet": true, + "final": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "newfail", + "search": "", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + }, + "events": [ + { + "name": "htmx:responseError", + "focus": "newfail", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=alpha&mode=guarded", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=gamma&mode=guarded&fail=1", + "status": 503 + }, + { + "event": "discard-navigation", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=guarded&hold=old" + }, + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=guarded&hold=old", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=guarded&hold=old", + "hx": true, + "history": false, + "status": 200, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=guarded&fail=1", + "hx": true, + "history": false, + "status": 503, + "held": false, + "released": true + } + ] + }, + { + "id": "O06-history-newer-than-request", + "engine": "chromium", + "mode": "guarded", + "status": "pass", + "before": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded alpha" + }, + "after": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded alpha" + }, + "obligations": { + "historySelection": true, + "historyContinuation": true + }, + "obligationsMet": true, + "final": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded alpha" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "search", + "title": "beta — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=guarded", + "feedback": "Loaded beta" + }, + { + "name": "htmx:afterSettle", + "focus": "old", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=alpha&mode=guarded", + "feedback": "Loaded beta" + }, + { + "name": "htmx:historyRestore", + "focus": "heading", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=alpha&mode=guarded", + "feedback": "Loaded alpha" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=guarded", + "status": 200 + }, + { + "event": "discard-navigation", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=guarded&hold=old" + }, + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=guarded&hold=old", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=guarded", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=guarded&hold=old", + "hx": true, + "history": false, + "status": 200, + "held": true, + "released": true + } + ] + }, + { + "id": "O01-history-focus-moved", + "engine": "firefox", + "mode": "managed", + "status": "pass", + "before": { + "topic": "beta", + "url": "/articles?topic=alpha&mode=managed", + "title": "beta — Catalog", + "focus": "search", + "search": "new task", + "feedback": "Loaded beta" + }, + "after": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=managed", + "title": "alpha — Catalog", + "focus": "heading", + "search": "new task", + "feedback": "Loaded alpha" + }, + "obligations": { + "selection": true, + "title": true, + "focus": false, + "typedText": true + }, + "obligationsMet": false, + "final": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=managed", + "title": "alpha — Catalog", + "focus": "heading", + "search": "new task", + "feedback": "Loaded alpha" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=managed", + "feedback": "Loaded beta" + }, + { + "name": "htmx:afterSettle", + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=alpha&mode=managed", + "feedback": "Loaded beta" + }, + { + "name": "htmx:historyRestore", + "focus": "heading", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=alpha&mode=managed", + "feedback": "Loaded alpha" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=managed", + "status": 200 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=alpha&mode=managed", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=managed", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": true, + "status": 200, + "held": true, + "released": true + } + ] + }, + { + "id": "O02-history-superseded", + "engine": "firefox", + "mode": "managed", + "status": "fail", + "error": "Cannot Tab to new", + "final": { + "topic": "beta", + "url": "/articles?topic=alpha&mode=managed", + "title": "beta — Catalog", + "focus": "search", + "search": "", + "feedback": "Loaded beta" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=managed", + "feedback": "Loaded beta" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=managed", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=managed", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": true, + "status": 200, + "held": true, + "released": true + } + ] + }, + { + "id": "O03-old-success", + "engine": "firefox", + "mode": "managed", + "status": "pass", + "before": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=managed", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "after": { + "topic": "beta", + "url": "/articles?topic=beta&mode=managed&hold=old", + "title": "beta — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded beta" + }, + "obligations": { + "newerSelection": false, + "noFeedbackOrFocusTheft": false + }, + "obligationsMet": false, + "final": { + "topic": "beta", + "url": "/articles?topic=beta&mode=managed&hold=old", + "title": "beta — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded beta" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "gamma — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=gamma&mode=managed", + "feedback": "Loaded gamma" + }, + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=managed&hold=old", + "feedback": "Loaded beta" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=gamma&mode=managed", + "status": 200 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=managed&hold=old", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=managed&hold=old", + "hx": true, + "history": false, + "status": 200, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=managed", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + } + ] + }, + { + "id": "O04-old-error", + "engine": "firefox", + "mode": "managed", + "status": "pass", + "before": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=managed", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "after": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=managed", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + }, + "obligations": { + "newerSelection": true, + "noObsoleteFeedback": false + }, + "obligationsMet": false, + "final": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=managed", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "gamma — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=gamma&mode=managed", + "feedback": "Loaded gamma" + }, + { + "name": "htmx:responseError", + "focus": "heading", + "title": "gamma — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=gamma&mode=managed", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=gamma&mode=managed", + "status": 200 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=managed&hold=old&fail=1", + "status": 503 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=managed&hold=old&fail=1", + "hx": true, + "history": false, + "status": 503, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=managed", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + } + ] + }, + { + "id": "O05-newer-fails", + "engine": "firefox", + "mode": "managed", + "status": "pass", + "before": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=managed", + "title": "alpha — Catalog", + "focus": "newfail", + "search": "", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + }, + "after": { + "topic": "beta", + "url": "/articles?topic=beta&mode=managed&hold=old", + "title": "beta — Catalog", + "focus": "newfail", + "search": "", + "feedback": "Loaded beta" + }, + "obligations": { + "committedView": false, + "newestFailureRetained": false + }, + "obligationsMet": false, + "final": { + "topic": "beta", + "url": "/articles?topic=beta&mode=managed&hold=old", + "title": "beta — Catalog", + "focus": "newfail", + "search": "", + "feedback": "Loaded beta" + }, + "events": [ + { + "name": "htmx:responseError", + "focus": "newfail", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=alpha&mode=managed", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + }, + { + "name": "htmx:afterSettle", + "focus": "newfail", + "title": "beta — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=managed&hold=old", + "feedback": "Loaded beta" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=gamma&mode=managed&fail=1", + "status": 503 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=managed&hold=old", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=managed&hold=old", + "hx": true, + "history": false, + "status": 200, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=managed&fail=1", + "hx": true, + "history": false, + "status": 503, + "held": false, + "released": true + } + ] + }, + { + "id": "O06-history-newer-than-request", + "engine": "firefox", + "mode": "managed", + "status": "fail", + "error": "Cannot Tab to old", + "final": { + "topic": "beta", + "url": "/articles?topic=beta&mode=managed", + "title": "beta — Catalog", + "focus": "search", + "search": "", + "feedback": "Loaded beta" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=managed", + "feedback": "Loaded beta" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=managed", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=managed", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + } + ] + }, + { + "id": "O01-history-focus-moved", + "engine": "firefox", + "mode": "guarded", + "status": "pass", + "before": { + "topic": "beta", + "url": "/articles?topic=alpha&mode=guarded", + "title": "beta — Catalog", + "focus": "search", + "search": "new task", + "feedback": "Loaded beta" + }, + "after": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "search", + "search": "new task", + "feedback": "Loaded alpha" + }, + "obligations": { + "selection": true, + "title": true, + "focus": true, + "typedText": true + }, + "obligationsMet": true, + "final": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "search", + "search": "new task", + "feedback": "Loaded alpha" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=guarded", + "feedback": "Loaded beta" + }, + { + "name": "htmx:afterSettle", + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=alpha&mode=guarded", + "feedback": "Loaded beta" + }, + { + "name": "htmx:historyRestore", + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=alpha&mode=guarded", + "feedback": "Loaded alpha" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=guarded", + "status": 200 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=alpha&mode=guarded", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=guarded", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": true, + "status": 200, + "held": true, + "released": true + } + ] + }, + { + "id": "O02-history-superseded", + "engine": "firefox", + "mode": "guarded", + "status": "fail", + "error": "Cannot Tab to new", + "final": { + "topic": "beta", + "url": "/articles?topic=alpha&mode=guarded", + "title": "beta — Catalog", + "focus": "search", + "search": "", + "feedback": "Loaded beta" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=guarded", + "feedback": "Loaded beta" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=guarded", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=guarded", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": true, + "status": 200, + "held": true, + "released": true + } + ] + }, + { + "id": "O03-old-success", + "engine": "firefox", + "mode": "guarded", + "status": "pass", + "before": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "after": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "obligations": { + "newerSelection": true, + "noFeedbackOrFocusTheft": true + }, + "obligationsMet": true, + "final": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "gamma — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=gamma&mode=guarded", + "feedback": "Loaded gamma" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=gamma&mode=guarded", + "status": 200 + }, + { + "event": "discard-navigation", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=guarded&hold=old" + }, + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=guarded&hold=old", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=guarded&hold=old", + "hx": true, + "history": false, + "status": 200, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=guarded", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + } + ] + }, + { + "id": "O04-old-error", + "engine": "firefox", + "mode": "guarded", + "status": "pass", + "before": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "after": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "obligations": { + "newerSelection": true, + "noObsoleteFeedback": true + }, + "obligationsMet": true, + "final": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "gamma — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=gamma&mode=guarded", + "feedback": "Loaded gamma" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=gamma&mode=guarded", + "status": 200 + }, + { + "event": "discard-navigation", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=guarded&hold=old&fail=1" + }, + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=guarded&hold=old&fail=1", + "status": 503 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=guarded&hold=old&fail=1", + "hx": true, + "history": false, + "status": 503, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=guarded", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + } + ] + }, + { + "id": "O05-newer-fails", + "engine": "firefox", + "mode": "guarded", + "status": "pass", + "before": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "newfail", + "search": "", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + }, + "after": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "newfail", + "search": "", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + }, + "obligations": { + "committedView": true, + "newestFailureRetained": true + }, + "obligationsMet": true, + "final": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "newfail", + "search": "", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + }, + "events": [ + { + "name": "htmx:responseError", + "focus": "newfail", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=alpha&mode=guarded", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=gamma&mode=guarded&fail=1", + "status": 503 + }, + { + "event": "discard-navigation", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=guarded&hold=old" + }, + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=guarded&hold=old", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=guarded&hold=old", + "hx": true, + "history": false, + "status": 200, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=guarded&fail=1", + "hx": true, + "history": false, + "status": 503, + "held": false, + "released": true + } + ] + }, + { + "id": "O06-history-newer-than-request", + "engine": "firefox", + "mode": "guarded", + "status": "fail", + "error": "Cannot Tab to old", + "final": { + "topic": "beta", + "url": "/articles?topic=beta&mode=guarded", + "title": "beta — Catalog", + "focus": "search", + "search": "", + "feedback": "Loaded beta" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=guarded", + "feedback": "Loaded beta" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:39063/articles?topic=beta&mode=guarded", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=guarded", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + } + ] + } + ], + "completed": 20, + "total": 24, + "obligationsMet": 10 +} diff --git a/research/experiments/navigation-history/ownership-initial.mjs b/research/experiments/navigation-history/ownership-initial.mjs new file mode 100644 index 0000000..c9de8f2 --- /dev/null +++ b/research/experiments/navigation-history/ownership-initial.mjs @@ -0,0 +1,64 @@ +import {chromium,firefox} from 'playwright'; +import {createServer} from 'node:http'; +import {once} from 'node:events'; +import {readFile,writeFile} from 'node:fs/promises'; +import {createHash} from 'node:crypto'; +import assert from 'node:assert/strict'; +const dir=new URL('.',import.meta.url); +const assets=new Map(await Promise.all([['/htmx.js','node_modules/htmx.org/dist/htmx.js'],['/baseline.js','focus-policy.js'],['/guarded.js','ownership-policy.js']].map(async([url,file])=>[url,await readFile(new URL(file,dir))]))); +const trace=`window.raceLog=[];for(const name of ['htmx:beforeRequest','htmx:historyCacheMiss'])document.addEventListener(name,e=>{const xhr=e.detail.xhr;xhr.addEventListener('load',()=>window.raceLog.push({event:'loaded',url:xhr.responseURL,status:xhr.status}));});`; +let requests=[],barriers=new Map(); +function barrier(key){let resolve;const reached=new Promise(r=>resolve=r);const b={reached,resolve,release:null};barriers.set(key,b);return b;} +const server=createServer((req,res)=>{ + const u=new URL(req.url,'http://local');res.setHeader('Cache-Control','no-store'); + if(assets.has(u.pathname)){res.setHeader('Content-Type','application/javascript');res.end(assets.get(u.pathname));return;} + if(u.pathname==='/trace.js'){res.setHeader('Content-Type','application/javascript');res.end(trace);return;} + if(u.pathname!=='/articles'){res.writeHead(404);res.end('Not found');return;} + const topic=['alpha','beta','gamma'].includes(u.searchParams.get('topic'))?u.searchParams.get('topic'):'alpha'; + const mode=u.searchParams.get('mode')==='guarded'?'guarded':'managed'; + const hx=req.headers['hx-request']==='true',history=req.headers['hx-history-restore-request']==='true'; + const status=hx&&u.searchParams.has('fail')?503:200; + const content=`

${topic}

Article ${topic}

`; + const boost='hx-boost="true" hx-target="#content" hx-select="#payload" hx-swap="innerHTML"'; + const href=(t,extra='')=>`/articles?topic=${t}&mode=${mode}${extra}`; + const nav=``; + const full=`${topic} — Catalog${nav}
${content}

`; + const record={path:req.url,hx,history,status,held:false,released:false};requests.push(record); + const send=()=>{record.released=true;res.writeHead(status,{'Content-Type':'text/html'});res.end(status===503?'Injected failure':hx&&!history?content:full);}; + const key=history?'history':u.searchParams.get('hold');const b=barriers.get(key); + if(b){record.held=true;b.release=send;b.resolve();}else send(); +}); +server.listen(0,'127.0.0.1');await once(server,'listening');const base=`http://127.0.0.1:${server.address().port}`; +const results=[],browsers={},output=process.env.FEP_RESULT_PATH||'/tmp/fep-ownership-result.json'; +const inspect=p=>p.evaluate(()=>({topic:document.querySelector('#heading')?.textContent,url:location.pathname+location.search,title:document.title,focus:document.activeElement.id||document.activeElement.tagName,search:document.querySelector('#search').value,feedback:document.querySelector('#feedback').textContent})); +async function tabTo(p,id){for(let n=0;n<15;n++){if((await inspect(p)).focus===id)return;await p.keyboard.press('Tab');}throw Error(`Cannot Tab to ${id}`);} +async function activate(p,id){await tabTo(p,id);await p.keyboard.press('Enter');} +async function selected(p,t){await p.waitForFunction(t=>document.querySelector('#heading')?.textContent===t,t);await p.waitForFunction(t=>document.querySelector('#feedback').textContent===`Loaded ${t}`,t);} +async function released(p,b,history=false){b.release();b.release=null;await p.waitForFunction(history=>window.raceLog.some(e=>e.event==='loaded'&&(history?e.url.includes('topic=alpha'):e.url.includes('hold=old'))),history);} +async function held(b){await Promise.race([b.reached,new Promise((_,reject)=>setTimeout(()=>reject(Error('Server barrier not reached')),4000))]);} +function same(a,b,keys){return keys.every(k=>a[k]===b[k]);} +try{ + for(const [engine,launcher]of [['chromium',chromium],['firefox',firefox]]){ + const path=engine==='chromium'?process.env.FEP_CHROMIUM_PATH:undefined;const browser=await launcher.launch({headless:true,...(path?{executablePath:path}:{})}); + browsers[engine]={version:browser.version(),executable:path||launcher.executablePath(),sha256:createHash('sha256').update(await readFile(path||launcher.executablePath())).digest('hex')}; + try{for(const mode of ['managed','guarded']){ + async function scenario(id,fn){ + requests=[];barriers=new Map();const context=await browser.newContext();const p=await context.newPage();p.setDefaultTimeout(4500); + const r={id,engine,mode,status:'pass'}; + try{await p.goto(base+`/articles?topic=alpha&mode=${mode}`);await p.waitForFunction(()=>window.htmx?.version==='2.0.11');Object.assign(r,await fn(p));r.obligationsMet=Object.values(r.obligations).every(Boolean);if(mode==='guarded')assert(r.obligationsMet,JSON.stringify(r.obligations));} + catch(e){r.status='fail';r.error=e.message;} + r.final=await inspect(p).catch(()=>null);r.events=await p.evaluate(()=>window.probe).catch(()=>[]);r.completions=await p.evaluate(()=>window.raceLog).catch(()=>[]);r.requests=[...requests];results.push(r); + for(const b of barriers.values())if(b.release){b.release();b.release=null;}await context.close();await writeFile(output,JSON.stringify({status:'incomplete',results},null,2)+'\n'); + } + async function backMiss(p){await activate(p,'beta');await selected(p,'beta');await p.evaluate(()=>sessionStorage.removeItem('htmx-history-cache'));const b=barrier('history');await p.goBack();await held(b);return b;} + await scenario('O01-history-focus-moved',async p=>{const b=await backMiss(p);await tabTo(p,'search');await p.keyboard.type('new task');const before=await inspect(p);await released(p,b,true);const after=await inspect(p);return {before,after,obligations:{selection:after.topic==='alpha'&&new URL(base+after.url).searchParams.get('topic')==='alpha',title:after.title==='alpha — Catalog',focus:after.focus==='search',typedText:after.search==='new task'}};}); + await scenario('O02-history-superseded',async p=>{const b=await backMiss(p);await activate(p,'new');await selected(p,'gamma');const before=await inspect(p);await released(p,b,true);const after=await inspect(p);return {before,after,obligations:{newerSelection:same(before,after,['topic','url','title']),noFeedbackOrFocusTheft:same(before,after,['feedback','focus'])}};}); + await scenario('O03-old-success',async p=>{const b=barrier('old');await activate(p,'old');await held(b);await activate(p,'new');await selected(p,'gamma');const before=await inspect(p);await released(p,b);const after=await inspect(p);return {before,after,obligations:{newerSelection:same(before,after,['topic','url','title']),noFeedbackOrFocusTheft:same(before,after,['feedback','focus'])}};}); + await scenario('O04-old-error',async p=>{const b=barrier('old');await activate(p,'oldfail');await held(b);await activate(p,'new');await selected(p,'gamma');const before=await inspect(p);await released(p,b);const after=await inspect(p);return {before,after,obligations:{newerSelection:same(before,after,['topic','url','title']),noObsoleteFeedback:same(before,after,['feedback','focus'])}};}); + await scenario('O05-newer-fails',async p=>{const b=barrier('old');await activate(p,'old');await held(b);await activate(p,'newfail');await p.waitForFunction(()=>document.querySelector('#feedback').textContent.includes('Navigation failed'));const before=await inspect(p);assert.equal(before.topic,'alpha');await released(p,b);const after=await inspect(p);return {before,after,obligations:{committedView:same(before,after,['topic','url','title']),newestFailureRetained:same(before,after,['feedback','focus'])}};}); + await scenario('O06-history-newer-than-request',async p=>{await activate(p,'beta');await selected(p,'beta');const b=barrier('old');await activate(p,'old');await held(b);await p.goBack();await selected(p,'alpha');const before=await inspect(p);await released(p,b);const after=await inspect(p);return {before,after,obligations:{historySelection:same(before,after,['topic','url','title']),historyContinuation:same(before,after,['feedback','focus'])}};}); + }}finally{await browser.close();} + } +}finally{for(const b of barriers.values())if(b.release)b.release();await new Promise(resolve=>server.close(resolve));} +const files={};for(const f of ['ownership.mjs','ownership-policy.js','focus-policy.js','package.json','package-lock.json','node_modules/htmx.org/dist/htmx.js'])files[f]=createHash('sha256').update(await readFile(new URL(f,dir))).digest('hex'); +const r={date:new Date().toISOString(),node:process.version,kind:'ordered-navigation-completion-races-in-authored-htmx-fixture',files,browsers,results,completed:results.filter(x=>x.status==='pass').length,total:results.length,obligationsMet:results.filter(x=>x.obligationsMet).length};await writeFile(output,JSON.stringify(r,null,2)+'\n');console.log(JSON.stringify({output,completed:r.completed,total:r.total,obligationsMet:r.obligationsMet,failures:results.filter(x=>x.status==='fail')},null,2));if(r.completed!==r.total)process.exitCode=1; diff --git a/research/experiments/navigation-history/ownership-policy.js b/research/experiments/navigation-history/ownership-policy.js new file mode 100644 index 0000000..9c49eb9 --- /dev/null +++ b/research/experiments/navigation-history/ownership-policy.js @@ -0,0 +1,31 @@ +// Experimental adapter pinned to htmx 2.0.11; not a production router. +window.probe=[]; +let generation=0,focusEpoch=0,historyMoveFocus=true; +const pending=new WeakMap(); +document.addEventListener('focusin',()=>focusEpoch++); +window.addEventListener('popstate',()=>{generation++;},true); +const notice=text=>{document.querySelector('#feedback').textContent=text;}; +function complete(move){const h=document.querySelector('#heading');document.title=`${h.textContent} — Catalog`;notice(`Loaded ${h.textContent}`);if(move)h.focus();} +document.addEventListener('htmx:beforeRequest',e=>{ + if(e.detail.target.id==='content')pending.set(e.detail.xhr,{owner:++generation,source:e.detail.elt,move:false}); +}); +document.addEventListener('htmx:beforeOnLoad',e=>{ + const state=pending.get(e.detail.xhr); + if(state&&state.owner!==generation){e.preventDefault();pending.delete(e.detail.xhr);window.raceLog.push({event:'discard-navigation',url:e.detail.xhr.responseURL});} +}); +document.addEventListener('htmx:beforeSwap',e=>{const state=pending.get(e.detail.xhr);if(state)state.move=document.activeElement===state.source;}); +document.addEventListener('htmx:afterSwap',e=>{const state=pending.get(e.detail.xhr);if(state){complete(state.move);pending.delete(e.detail.xhr);}}); +document.addEventListener('htmx:responseError',e=>{if(pending.has(e.detail.xhr)){notice('Navigation failed. Current article retained. Use Native recovery to continue.');pending.delete(e.detail.xhr);}}); +document.addEventListener('htmx:historyCacheHit',()=>{historyMoveFocus=true;}); +document.addEventListener('htmx:historyCacheMiss',e=>{ + const owner=generation,focusAtStart=focusEpoch,path=e.detail.path; + const xhr=e.detail.xhr,original=xhr.onload; + // The public miss event exposes XHR; wrapping its existing handler relies on the pinned implementation. + xhr.onload=function(event){ + if(owner!==generation||location.pathname+location.search!==path){window.raceLog.push({event:'discard-history',url:xhr.responseURL});return;} + historyMoveFocus=focusEpoch===focusAtStart; + return original.call(this,event); + }; +}); +document.addEventListener('htmx:historyRestore',()=>complete(historyMoveFocus)); +for(const name of ['htmx:afterSettle','htmx:historyRestore','htmx:responseError'])document.addEventListener(name,()=>window.probe.push({name,focus:document.activeElement.id||document.activeElement.tagName,title:document.title,url:location.href,feedback:document.querySelector('#feedback').textContent})); diff --git a/research/experiments/navigation-history/ownership-result.json b/research/experiments/navigation-history/ownership-result.json new file mode 100644 index 0000000..529eca1 --- /dev/null +++ b/research/experiments/navigation-history/ownership-result.json @@ -0,0 +1,2301 @@ +{ + "date": "2026-09-26T22:41:46.321Z", + "node": "v24.11.1", + "kind": "ordered-navigation-completion-races-in-authored-htmx-fixture", + "files": { + "ownership.mjs": "ad6e77d3e1704aeea13db9fcf5975bc7fa9f996b41a01ef1449a9f20f2c82e44", + "ownership-policy.js": "154cd4aafe4c77ba826a1bc2e6eb0d728e4a9169f23dbfe8ee3c377744273dd9", + "focus-policy.js": "cb91524d66cfff97b0bed905502479ab3134eecdfb6964a3b226d516476b145a", + "package.json": "373e05f4d2b9e043abc9391e6ec28bcf6ffdd5530e52f3e5db88ed20e4462ed5", + "package-lock.json": "6fb5a45148a199c10fd9b67b1d5468a0334e5fcc154f1f079fd88462a89328d3", + "node_modules/htmx.org/dist/htmx.js": "30612ca7e3d3baf407481f1798281c4481dcb93390a9722d328ce07290250426" + }, + "browsers": { + "chromium": { + "version": "151.0.7922.34", + "executable": "/home/worsch/.cache/ms-playwright/chromium-1234/chrome-linux64/chrome", + "sha256": "0b20b130e7edd9dd51873be867761295fe0cfad490c2b9a64f95bd3cfc08fa71" + }, + "firefox": { + "version": "155.0", + "executable": "/tmp/fep-playwright-browsers/firefox-1543/firefox/firefox", + "sha256": "062272ac4d851eade5b75db2dc7c14569d96cb3ca38f949337cdc1b74367e35b" + } + }, + "results": [ + { + "id": "O01-history-focus-moved", + "engine": "chromium", + "mode": "managed", + "status": "pass", + "before": { + "topic": "beta", + "url": "/articles?topic=alpha&mode=managed", + "title": "beta — Catalog", + "focus": "search", + "search": "new task", + "feedback": "Loaded beta" + }, + "after": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=managed", + "title": "alpha — Catalog", + "focus": "heading", + "search": "new task", + "feedback": "Loaded alpha" + }, + "obligations": { + "selection": true, + "title": true, + "focus": false, + "typedText": true + }, + "obligationsMet": false, + "final": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=managed", + "title": "alpha — Catalog", + "focus": "heading", + "search": "new task", + "feedback": "Loaded alpha" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=managed", + "feedback": "Loaded beta" + }, + { + "name": "htmx:afterSettle", + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=managed", + "feedback": "Loaded beta" + }, + { + "name": "htmx:historyRestore", + "focus": "heading", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=managed", + "feedback": "Loaded alpha" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=managed", + "status": 200 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=managed", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=managed", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": true, + "status": 200, + "held": true, + "released": true + } + ] + }, + { + "id": "O02-history-superseded", + "engine": "chromium", + "mode": "managed", + "status": "pass", + "before": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=managed", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "after": { + "topic": "alpha", + "url": "/articles?topic=gamma&mode=managed", + "title": "alpha — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded alpha" + }, + "obligations": { + "newerSelection": false, + "noFeedbackOrFocusTheft": false + }, + "obligationsMet": false, + "final": { + "topic": "alpha", + "url": "/articles?topic=gamma&mode=managed", + "title": "alpha — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded alpha" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=managed", + "feedback": "Loaded beta" + }, + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "gamma — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=managed", + "feedback": "Loaded gamma" + }, + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=managed", + "feedback": "Loaded gamma" + }, + { + "name": "htmx:historyRestore", + "focus": "heading", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=managed", + "feedback": "Loaded alpha" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=managed", + "status": 200 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=managed", + "status": 200 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=managed", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=managed", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": true, + "status": 200, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=managed", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + } + ] + }, + { + "id": "O03-old-success", + "engine": "chromium", + "mode": "managed", + "status": "pass", + "before": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=managed", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "after": { + "topic": "beta", + "url": "/articles?topic=beta&mode=managed&hold=old", + "title": "beta — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded beta" + }, + "obligations": { + "newerSelection": false, + "noFeedbackOrFocusTheft": false + }, + "obligationsMet": false, + "final": { + "topic": "beta", + "url": "/articles?topic=beta&mode=managed&hold=old", + "title": "beta — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded beta" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "gamma — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=managed", + "feedback": "Loaded gamma" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=managed", + "status": 200 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=managed&hold=old", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=managed&hold=old", + "hx": true, + "history": false, + "status": 200, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=managed", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + } + ] + }, + { + "id": "O04-old-error", + "engine": "chromium", + "mode": "managed", + "status": "pass", + "before": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=managed", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "after": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=managed", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + }, + "obligations": { + "newerSelection": true, + "noObsoleteFeedback": false + }, + "obligationsMet": false, + "final": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=managed", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "gamma — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=managed", + "feedback": "Loaded gamma" + }, + { + "name": "htmx:responseError", + "focus": "heading", + "title": "gamma — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=managed", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=managed", + "status": 200 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=managed&hold=old&fail=1", + "status": 503 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=managed&hold=old&fail=1", + "hx": true, + "history": false, + "status": 503, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=managed", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + } + ] + }, + { + "id": "O05-newer-fails", + "engine": "chromium", + "mode": "managed", + "status": "pass", + "before": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=managed", + "title": "alpha — Catalog", + "focus": "newfail", + "search": "", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + }, + "after": { + "topic": "beta", + "url": "/articles?topic=beta&mode=managed&hold=old", + "title": "beta — Catalog", + "focus": "newfail", + "search": "", + "feedback": "Loaded beta" + }, + "obligations": { + "committedView": false, + "newestFailureRetained": false + }, + "obligationsMet": false, + "final": { + "topic": "beta", + "url": "/articles?topic=beta&mode=managed&hold=old", + "title": "beta — Catalog", + "focus": "newfail", + "search": "", + "feedback": "Loaded beta" + }, + "events": [ + { + "name": "htmx:responseError", + "focus": "newfail", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=managed", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=managed&fail=1", + "status": 503 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=managed&hold=old", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=managed&hold=old", + "hx": true, + "history": false, + "status": 200, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=managed&fail=1", + "hx": true, + "history": false, + "status": 503, + "held": false, + "released": true + } + ] + }, + { + "id": "O06-history-newer-than-request", + "engine": "chromium", + "mode": "managed", + "status": "pass", + "before": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=managed", + "title": "alpha — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded alpha" + }, + "after": { + "topic": "beta", + "url": "/articles?topic=beta&mode=managed&hold=old", + "title": "beta — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded beta" + }, + "obligations": { + "historySelection": false, + "historyContinuation": false + }, + "obligationsMet": false, + "final": { + "topic": "beta", + "url": "/articles?topic=beta&mode=managed&hold=old", + "title": "beta — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded beta" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=managed", + "feedback": "Loaded beta" + }, + { + "name": "htmx:afterSettle", + "focus": "old", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=managed", + "feedback": "Loaded beta" + }, + { + "name": "htmx:historyRestore", + "focus": "heading", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=managed", + "feedback": "Loaded alpha" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=managed", + "status": 200 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=managed&hold=old", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=managed", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=managed&hold=old", + "hx": true, + "history": false, + "status": 200, + "held": true, + "released": true + } + ] + }, + { + "id": "O01-history-focus-moved", + "engine": "chromium", + "mode": "guarded", + "status": "pass", + "before": { + "topic": "beta", + "url": "/articles?topic=alpha&mode=guarded", + "title": "beta — Catalog", + "focus": "search", + "search": "new task", + "feedback": "Loaded beta" + }, + "after": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "search", + "search": "new task", + "feedback": "Loaded alpha" + }, + "obligations": { + "selection": true, + "title": true, + "focus": true, + "typedText": true + }, + "obligationsMet": true, + "final": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "search", + "search": "new task", + "feedback": "Loaded alpha" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=guarded", + "feedback": "Loaded beta" + }, + { + "name": "htmx:afterSettle", + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=guarded", + "feedback": "Loaded beta" + }, + { + "name": "htmx:historyRestore", + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=guarded", + "feedback": "Loaded alpha" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=guarded", + "status": 200 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=guarded", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=guarded", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": true, + "status": 200, + "held": true, + "released": true + } + ] + }, + { + "id": "O02-history-superseded", + "engine": "chromium", + "mode": "guarded", + "status": "pass", + "before": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "after": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "obligations": { + "newerSelection": true, + "noFeedbackOrFocusTheft": true + }, + "obligationsMet": true, + "final": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=guarded", + "feedback": "Loaded beta" + }, + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "gamma — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=guarded", + "feedback": "Loaded gamma" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=guarded", + "status": 200 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=guarded", + "status": 200 + }, + { + "event": "discard-history", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=guarded" + }, + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=guarded", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=guarded", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": true, + "status": 200, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=guarded", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + } + ] + }, + { + "id": "O03-old-success", + "engine": "chromium", + "mode": "guarded", + "status": "pass", + "before": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "after": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "obligations": { + "newerSelection": true, + "noFeedbackOrFocusTheft": true + }, + "obligationsMet": true, + "final": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "gamma — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=guarded", + "feedback": "Loaded gamma" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=guarded", + "status": 200 + }, + { + "event": "discard-navigation", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=guarded&hold=old" + }, + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=guarded&hold=old", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=guarded&hold=old", + "hx": true, + "history": false, + "status": 200, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=guarded", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + } + ] + }, + { + "id": "O04-old-error", + "engine": "chromium", + "mode": "guarded", + "status": "pass", + "before": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "after": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "obligations": { + "newerSelection": true, + "noObsoleteFeedback": true + }, + "obligationsMet": true, + "final": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "gamma — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=guarded", + "feedback": "Loaded gamma" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=guarded", + "status": 200 + }, + { + "event": "discard-navigation", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=guarded&hold=old&fail=1" + }, + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=guarded&hold=old&fail=1", + "status": 503 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=guarded&hold=old&fail=1", + "hx": true, + "history": false, + "status": 503, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=guarded", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + } + ] + }, + { + "id": "O05-newer-fails", + "engine": "chromium", + "mode": "guarded", + "status": "pass", + "before": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "newfail", + "search": "", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + }, + "after": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "newfail", + "search": "", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + }, + "obligations": { + "committedView": true, + "newestFailureRetained": true + }, + "obligationsMet": true, + "final": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "newfail", + "search": "", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + }, + "events": [ + { + "name": "htmx:responseError", + "focus": "newfail", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=guarded", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=guarded&fail=1", + "status": 503 + }, + { + "event": "discard-navigation", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=guarded&hold=old" + }, + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=guarded&hold=old", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=guarded&hold=old", + "hx": true, + "history": false, + "status": 200, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=guarded&fail=1", + "hx": true, + "history": false, + "status": 503, + "held": false, + "released": true + } + ] + }, + { + "id": "O06-history-newer-than-request", + "engine": "chromium", + "mode": "guarded", + "status": "pass", + "before": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded alpha" + }, + "after": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded alpha" + }, + "obligations": { + "historySelection": true, + "historyContinuation": true + }, + "obligationsMet": true, + "final": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded alpha" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=guarded", + "feedback": "Loaded beta" + }, + { + "name": "htmx:afterSettle", + "focus": "old", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=guarded", + "feedback": "Loaded beta" + }, + { + "name": "htmx:historyRestore", + "focus": "heading", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=guarded", + "feedback": "Loaded alpha" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=guarded", + "status": 200 + }, + { + "event": "discard-navigation", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=guarded&hold=old" + }, + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=guarded&hold=old", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=guarded", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=guarded&hold=old", + "hx": true, + "history": false, + "status": 200, + "held": true, + "released": true + } + ] + }, + { + "id": "O01-history-focus-moved", + "engine": "firefox", + "mode": "managed", + "status": "pass", + "before": { + "topic": "beta", + "url": "/articles?topic=alpha&mode=managed", + "title": "beta — Catalog", + "focus": "search", + "search": "new task", + "feedback": "Loaded beta" + }, + "after": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=managed", + "title": "alpha — Catalog", + "focus": "heading", + "search": "new task", + "feedback": "Loaded alpha" + }, + "obligations": { + "selection": true, + "title": true, + "focus": false, + "typedText": true + }, + "obligationsMet": false, + "final": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=managed", + "title": "alpha — Catalog", + "focus": "heading", + "search": "new task", + "feedback": "Loaded alpha" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=managed", + "feedback": "Loaded beta" + }, + { + "name": "htmx:afterSettle", + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=managed", + "feedback": "Loaded beta" + }, + { + "name": "htmx:historyRestore", + "focus": "heading", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=managed", + "feedback": "Loaded alpha" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=managed", + "status": 200 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=managed", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=managed", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": true, + "status": 200, + "held": true, + "released": true + } + ] + }, + { + "id": "O02-history-superseded", + "engine": "firefox", + "mode": "managed", + "status": "pass", + "before": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=managed", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "after": { + "topic": "alpha", + "url": "/articles?topic=gamma&mode=managed", + "title": "alpha — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded alpha" + }, + "obligations": { + "newerSelection": false, + "noFeedbackOrFocusTheft": false + }, + "obligationsMet": false, + "final": { + "topic": "alpha", + "url": "/articles?topic=gamma&mode=managed", + "title": "alpha — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded alpha" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=managed", + "feedback": "Loaded beta" + }, + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "gamma — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=managed", + "feedback": "Loaded gamma" + }, + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=managed", + "feedback": "Loaded gamma" + }, + { + "name": "htmx:historyRestore", + "focus": "heading", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=managed", + "feedback": "Loaded alpha" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=managed", + "status": 200 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=managed", + "status": 200 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=managed", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=managed", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": true, + "status": 200, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=managed", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + } + ] + }, + { + "id": "O03-old-success", + "engine": "firefox", + "mode": "managed", + "status": "pass", + "before": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=managed", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "after": { + "topic": "beta", + "url": "/articles?topic=beta&mode=managed&hold=old", + "title": "beta — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded beta" + }, + "obligations": { + "newerSelection": false, + "noFeedbackOrFocusTheft": false + }, + "obligationsMet": false, + "final": { + "topic": "beta", + "url": "/articles?topic=beta&mode=managed&hold=old", + "title": "beta — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded beta" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "gamma — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=managed", + "feedback": "Loaded gamma" + }, + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=managed&hold=old", + "feedback": "Loaded beta" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=managed", + "status": 200 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=managed&hold=old", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=managed&hold=old", + "hx": true, + "history": false, + "status": 200, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=managed", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + } + ] + }, + { + "id": "O04-old-error", + "engine": "firefox", + "mode": "managed", + "status": "pass", + "before": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=managed", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "after": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=managed", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + }, + "obligations": { + "newerSelection": true, + "noObsoleteFeedback": false + }, + "obligationsMet": false, + "final": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=managed", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "gamma — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=managed", + "feedback": "Loaded gamma" + }, + { + "name": "htmx:responseError", + "focus": "heading", + "title": "gamma — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=managed", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=managed", + "status": 200 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=managed&hold=old&fail=1", + "status": 503 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=managed&hold=old&fail=1", + "hx": true, + "history": false, + "status": 503, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=managed", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + } + ] + }, + { + "id": "O05-newer-fails", + "engine": "firefox", + "mode": "managed", + "status": "pass", + "before": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=managed", + "title": "alpha — Catalog", + "focus": "newfail", + "search": "", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + }, + "after": { + "topic": "beta", + "url": "/articles?topic=beta&mode=managed&hold=old", + "title": "beta — Catalog", + "focus": "newfail", + "search": "", + "feedback": "Loaded beta" + }, + "obligations": { + "committedView": false, + "newestFailureRetained": false + }, + "obligationsMet": false, + "final": { + "topic": "beta", + "url": "/articles?topic=beta&mode=managed&hold=old", + "title": "beta — Catalog", + "focus": "newfail", + "search": "", + "feedback": "Loaded beta" + }, + "events": [ + { + "name": "htmx:responseError", + "focus": "newfail", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=managed", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + }, + { + "name": "htmx:afterSettle", + "focus": "newfail", + "title": "beta — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=managed&hold=old", + "feedback": "Loaded beta" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=managed&fail=1", + "status": 503 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=managed&hold=old", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=managed&hold=old", + "hx": true, + "history": false, + "status": 200, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=managed&fail=1", + "hx": true, + "history": false, + "status": 503, + "held": false, + "released": true + } + ] + }, + { + "id": "O06-history-newer-than-request", + "engine": "firefox", + "mode": "managed", + "status": "pass", + "before": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=managed", + "title": "alpha — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded alpha" + }, + "after": { + "topic": "beta", + "url": "/articles?topic=beta&mode=managed&hold=old", + "title": "beta — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded beta" + }, + "obligations": { + "historySelection": false, + "historyContinuation": false + }, + "obligationsMet": false, + "final": { + "topic": "beta", + "url": "/articles?topic=beta&mode=managed&hold=old", + "title": "beta — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded beta" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=managed", + "feedback": "Loaded beta" + }, + { + "name": "htmx:afterSettle", + "focus": "old", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=managed", + "feedback": "Loaded beta" + }, + { + "name": "htmx:historyRestore", + "focus": "heading", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=managed", + "feedback": "Loaded alpha" + }, + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=managed&hold=old", + "feedback": "Loaded beta" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=managed", + "status": 200 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=managed&hold=old", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=managed", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=managed&hold=old", + "hx": true, + "history": false, + "status": 200, + "held": true, + "released": true + } + ] + }, + { + "id": "O01-history-focus-moved", + "engine": "firefox", + "mode": "guarded", + "status": "pass", + "before": { + "topic": "beta", + "url": "/articles?topic=alpha&mode=guarded", + "title": "beta — Catalog", + "focus": "search", + "search": "new task", + "feedback": "Loaded beta" + }, + "after": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "search", + "search": "new task", + "feedback": "Loaded alpha" + }, + "obligations": { + "selection": true, + "title": true, + "focus": true, + "typedText": true + }, + "obligationsMet": true, + "final": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "search", + "search": "new task", + "feedback": "Loaded alpha" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=guarded", + "feedback": "Loaded beta" + }, + { + "name": "htmx:afterSettle", + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=guarded", + "feedback": "Loaded beta" + }, + { + "name": "htmx:historyRestore", + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=guarded", + "feedback": "Loaded alpha" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=guarded", + "status": 200 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=guarded", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=guarded", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": true, + "status": 200, + "held": true, + "released": true + } + ] + }, + { + "id": "O02-history-superseded", + "engine": "firefox", + "mode": "guarded", + "status": "pass", + "before": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "after": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "obligations": { + "newerSelection": true, + "noFeedbackOrFocusTheft": true + }, + "obligationsMet": true, + "final": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=guarded", + "feedback": "Loaded beta" + }, + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "gamma — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=guarded", + "feedback": "Loaded gamma" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=guarded", + "status": 200 + }, + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=guarded", + "status": 200 + }, + { + "event": "discard-history", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=guarded" + }, + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=guarded", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=guarded", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": true, + "status": 200, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=guarded", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + } + ] + }, + { + "id": "O03-old-success", + "engine": "firefox", + "mode": "guarded", + "status": "pass", + "before": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "after": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "obligations": { + "newerSelection": true, + "noFeedbackOrFocusTheft": true + }, + "obligationsMet": true, + "final": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "gamma — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=guarded", + "feedback": "Loaded gamma" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=guarded", + "status": 200 + }, + { + "event": "discard-navigation", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=guarded&hold=old" + }, + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=guarded&hold=old", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=guarded&hold=old", + "hx": true, + "history": false, + "status": 200, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=guarded", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + } + ] + }, + { + "id": "O04-old-error", + "engine": "firefox", + "mode": "guarded", + "status": "pass", + "before": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "after": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "obligations": { + "newerSelection": true, + "noObsoleteFeedback": true + }, + "obligationsMet": true, + "final": { + "topic": "gamma", + "url": "/articles?topic=gamma&mode=guarded", + "title": "gamma — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded gamma" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "gamma — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=guarded", + "feedback": "Loaded gamma" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=guarded", + "status": 200 + }, + { + "event": "discard-navigation", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=guarded&hold=old&fail=1" + }, + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=guarded&hold=old&fail=1", + "status": 503 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=guarded&hold=old&fail=1", + "hx": true, + "history": false, + "status": 503, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=guarded", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + } + ] + }, + { + "id": "O05-newer-fails", + "engine": "firefox", + "mode": "guarded", + "status": "pass", + "before": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "newfail", + "search": "", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + }, + "after": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "newfail", + "search": "", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + }, + "obligations": { + "committedView": true, + "newestFailureRetained": true + }, + "obligationsMet": true, + "final": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "newfail", + "search": "", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + }, + "events": [ + { + "name": "htmx:responseError", + "focus": "newfail", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=guarded", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue." + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=gamma&mode=guarded&fail=1", + "status": 503 + }, + { + "event": "discard-navigation", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=guarded&hold=old" + }, + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=guarded&hold=old", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=guarded&hold=old", + "hx": true, + "history": false, + "status": 200, + "held": true, + "released": true + }, + { + "path": "/articles?topic=gamma&mode=guarded&fail=1", + "hx": true, + "history": false, + "status": 503, + "held": false, + "released": true + } + ] + }, + { + "id": "O06-history-newer-than-request", + "engine": "firefox", + "mode": "guarded", + "status": "pass", + "before": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded alpha" + }, + "after": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded alpha" + }, + "obligations": { + "historySelection": true, + "historyContinuation": true + }, + "obligationsMet": true, + "final": { + "topic": "alpha", + "url": "/articles?topic=alpha&mode=guarded", + "title": "alpha — Catalog", + "focus": "heading", + "search": "", + "feedback": "Loaded alpha" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=guarded", + "feedback": "Loaded beta" + }, + { + "name": "htmx:afterSettle", + "focus": "old", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=guarded", + "feedback": "Loaded beta" + }, + { + "name": "htmx:historyRestore", + "focus": "heading", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:36469/articles?topic=alpha&mode=guarded", + "feedback": "Loaded alpha" + } + ], + "completions": [ + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=guarded", + "status": 200 + }, + { + "event": "discard-navigation", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=guarded&hold=old" + }, + { + "event": "loaded", + "url": "http://127.0.0.1:36469/articles?topic=beta&mode=guarded&hold=old", + "status": 200 + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=guarded", + "hx": false, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=guarded", + "hx": true, + "history": false, + "status": 200, + "held": false, + "released": true + }, + { + "path": "/articles?topic=beta&mode=guarded&hold=old", + "hx": true, + "history": false, + "status": 200, + "held": true, + "released": true + } + ] + } + ], + "completed": 24, + "total": 24, + "obligationsMet": 12 +} diff --git a/research/experiments/navigation-history/ownership.mjs b/research/experiments/navigation-history/ownership.mjs new file mode 100644 index 0000000..cf142b4 --- /dev/null +++ b/research/experiments/navigation-history/ownership.mjs @@ -0,0 +1,64 @@ +import {chromium,firefox} from 'playwright'; +import {createServer} from 'node:http'; +import {once} from 'node:events'; +import {readFile,writeFile} from 'node:fs/promises'; +import {createHash} from 'node:crypto'; +import assert from 'node:assert/strict'; +const dir=new URL('.',import.meta.url); +const assets=new Map(await Promise.all([['/htmx.js','node_modules/htmx.org/dist/htmx.js'],['/baseline.js','focus-policy.js'],['/guarded.js','ownership-policy.js']].map(async([url,file])=>[url,await readFile(new URL(file,dir))]))); +const trace=`window.raceLog=[];for(const name of ['htmx:beforeRequest','htmx:historyCacheMiss'])document.addEventListener(name,e=>{const xhr=e.detail.xhr;xhr.addEventListener('load',()=>window.raceLog.push({event:'loaded',url:xhr.responseURL,status:xhr.status}));});`; +let requests=[],barriers=new Map(); +function barrier(key){let resolve;const reached=new Promise(r=>resolve=r);const b={reached,resolve,release:null};barriers.set(key,b);return b;} +const server=createServer((req,res)=>{ + const u=new URL(req.url,'http://local');res.setHeader('Cache-Control','no-store'); + if(assets.has(u.pathname)){res.setHeader('Content-Type','application/javascript');res.end(assets.get(u.pathname));return;} + if(u.pathname==='/trace.js'){res.setHeader('Content-Type','application/javascript');res.end(trace);return;} + if(u.pathname!=='/articles'){res.writeHead(404);res.end('Not found');return;} + const topic=['alpha','beta','gamma'].includes(u.searchParams.get('topic'))?u.searchParams.get('topic'):'alpha'; + const mode=u.searchParams.get('mode')==='guarded'?'guarded':'managed'; + const hx=req.headers['hx-request']==='true',history=req.headers['hx-history-restore-request']==='true'; + const status=hx&&u.searchParams.has('fail')?503:200; + const content=`

${topic}

Article ${topic}

`; + const boost='hx-boost="true" hx-target="#content" hx-select="#payload" hx-swap="innerHTML"'; + const href=(t,extra='')=>`/articles?topic=${t}&mode=${mode}${extra}`; + const nav=``; + const full=`${topic} — Catalog${nav}
${content}

`; + const record={path:req.url,hx,history,status,held:false,released:false};requests.push(record); + const send=()=>{record.released=true;res.writeHead(status,{'Content-Type':'text/html'});res.end(status===503?'Injected failure':hx&&!history?content:full);}; + const key=history?'history':u.searchParams.get('hold');const b=barriers.get(key); + if(b){record.held=true;b.release=send;b.resolve();}else send(); +}); +server.listen(0,'127.0.0.1');await once(server,'listening');const base=`http://127.0.0.1:${server.address().port}`; +const results=[],browsers={},output=process.env.FEP_RESULT_PATH||'/tmp/fep-ownership-result.json'; +const inspect=p=>p.evaluate(()=>({topic:document.querySelector('#heading')?.textContent,url:location.pathname+location.search,title:document.title,focus:document.activeElement.id||document.activeElement.tagName,search:document.querySelector('#search').value,feedback:document.querySelector('#feedback').textContent})); +async function tabTo(p,id){for(let n=0;n<15;n++){if((await inspect(p)).focus===id)return;const backwards=await p.evaluate(id=>{const a=document.activeElement,t=document.getElementById(id);return a!==document.body&&Boolean(a.compareDocumentPosition(t)&Node.DOCUMENT_POSITION_PRECEDING);},id);await p.keyboard.press(backwards?'Shift+Tab':'Tab');}throw Error(`Cannot Tab to ${id}`);} +async function activate(p,id){await tabTo(p,id);await p.keyboard.press('Enter');} +async function selected(p,t){await p.waitForFunction(t=>document.querySelector('#heading')?.textContent===t,t);await p.waitForFunction(t=>document.querySelector('#feedback').textContent===`Loaded ${t}`,t);} +async function released(p,b,history=false){b.release();b.release=null;await p.waitForFunction(history=>window.raceLog.some(e=>e.event==='loaded'&&(history?e.url.includes('topic=alpha'):e.url.includes('hold=old'))),history);} +async function held(b){await Promise.race([b.reached,new Promise((_,reject)=>setTimeout(()=>reject(Error('Server barrier not reached')),4000))]);} +function same(a,b,keys){return keys.every(k=>a[k]===b[k]);} +try{ + for(const [engine,launcher]of [['chromium',chromium],['firefox',firefox]]){ + const path=engine==='chromium'?process.env.FEP_CHROMIUM_PATH:undefined;const browser=await launcher.launch({headless:true,...(path?{executablePath:path}:{})}); + browsers[engine]={version:browser.version(),executable:path||launcher.executablePath(),sha256:createHash('sha256').update(await readFile(path||launcher.executablePath())).digest('hex')}; + try{for(const mode of ['managed','guarded']){ + async function scenario(id,fn){ + requests=[];barriers=new Map();const context=await browser.newContext();const p=await context.newPage();p.setDefaultTimeout(4500); + const r={id,engine,mode,status:'pass'}; + try{await p.goto(base+`/articles?topic=alpha&mode=${mode}`);await p.waitForFunction(()=>window.htmx?.version==='2.0.11');Object.assign(r,await fn(p));r.obligationsMet=Object.values(r.obligations).every(Boolean);if(mode==='guarded')assert(r.obligationsMet,JSON.stringify(r.obligations));} + catch(e){r.status='fail';r.error=e.message;} + r.final=await inspect(p).catch(()=>null);r.events=await p.evaluate(()=>window.probe).catch(()=>[]);r.completions=await p.evaluate(()=>window.raceLog).catch(()=>[]);r.requests=[...requests];results.push(r); + for(const b of barriers.values())if(b.release){b.release();b.release=null;}await context.close();await writeFile(output,JSON.stringify({status:'incomplete',results},null,2)+'\n'); + } + async function backMiss(p){await activate(p,'beta');await selected(p,'beta');await p.evaluate(()=>sessionStorage.removeItem('htmx-history-cache'));const b=barrier('history');await p.goBack();await held(b);return b;} + await scenario('O01-history-focus-moved',async p=>{const b=await backMiss(p);await tabTo(p,'search');await p.keyboard.type('new task');const before=await inspect(p);await released(p,b,true);const after=await inspect(p);return {before,after,obligations:{selection:after.topic==='alpha'&&new URL(base+after.url).searchParams.get('topic')==='alpha',title:after.title==='alpha — Catalog',focus:after.focus==='search',typedText:after.search==='new task'}};}); + await scenario('O02-history-superseded',async p=>{const b=await backMiss(p);await activate(p,'new');await selected(p,'gamma');const before=await inspect(p);await released(p,b,true);const after=await inspect(p);return {before,after,obligations:{newerSelection:same(before,after,['topic','url','title']),noFeedbackOrFocusTheft:same(before,after,['feedback','focus'])}};}); + await scenario('O03-old-success',async p=>{const b=barrier('old');await activate(p,'old');await held(b);await activate(p,'new');await selected(p,'gamma');const before=await inspect(p);await released(p,b);const after=await inspect(p);return {before,after,obligations:{newerSelection:same(before,after,['topic','url','title']),noFeedbackOrFocusTheft:same(before,after,['feedback','focus'])}};}); + await scenario('O04-old-error',async p=>{const b=barrier('old');await activate(p,'oldfail');await held(b);await activate(p,'new');await selected(p,'gamma');const before=await inspect(p);await released(p,b);const after=await inspect(p);return {before,after,obligations:{newerSelection:same(before,after,['topic','url','title']),noObsoleteFeedback:same(before,after,['feedback','focus'])}};}); + await scenario('O05-newer-fails',async p=>{const b=barrier('old');await activate(p,'old');await held(b);await activate(p,'newfail');await p.waitForFunction(()=>document.querySelector('#feedback').textContent.includes('Navigation failed'));const before=await inspect(p);assert.equal(before.topic,'alpha');await released(p,b);const after=await inspect(p);return {before,after,obligations:{committedView:same(before,after,['topic','url','title']),newestFailureRetained:same(before,after,['feedback','focus'])}};}); + await scenario('O06-history-newer-than-request',async p=>{await activate(p,'beta');await selected(p,'beta');const b=barrier('old');await activate(p,'old');await held(b);await p.goBack();await selected(p,'alpha');const before=await inspect(p);await released(p,b);const after=await inspect(p);return {before,after,obligations:{historySelection:same(before,after,['topic','url','title']),historyContinuation:same(before,after,['feedback','focus'])}};}); + }}finally{await browser.close();} + } +}finally{for(const b of barriers.values())if(b.release)b.release();await new Promise(resolve=>server.close(resolve));} +const files={};for(const f of ['ownership.mjs','ownership-policy.js','focus-policy.js','package.json','package-lock.json','node_modules/htmx.org/dist/htmx.js'])files[f]=createHash('sha256').update(await readFile(new URL(f,dir))).digest('hex'); +const r={date:new Date().toISOString(),node:process.version,kind:'ordered-navigation-completion-races-in-authored-htmx-fixture',files,browsers,results,completed:results.filter(x=>x.status==='pass').length,total:results.length,obligationsMet:results.filter(x=>x.obligationsMet).length};await writeFile(output,JSON.stringify(r,null,2)+'\n');console.log(JSON.stringify({output,completed:r.completed,total:r.total,obligationsMet:r.obligationsMet,failures:results.filter(x=>x.status==='fail')},null,2));if(r.completed!==r.total)process.exitCode=1; diff --git a/research/plan/OpenQuestions.md b/research/plan/OpenQuestions.md index cb15472..c5eb679 100644 --- a/research/plan/OpenQuestions.md +++ b/research/plan/OpenQuestions.md @@ -114,3 +114,12 @@ Q06 activation/reload follow-up: [conditional update and draft tests](../reviews show why disjoint-intent retry differs from competing rollback, and why preflight compatibility must be rechecked after reload. Same-tab draft preservation is narrower than durability or remote-outcome recovery. No question or maturity gate is closed. + +## Q05 navigation completion versus focus ownership — 2026-09-27 + +[NO01–NO06](../reviews/NavigationOwnershipExperiment.md) distinguish rights to apply +a navigation result from rights to move focus. Obsolete errors and history completions +can violate current intent even when the prior single-response policy seemed adequate. +Q05 remains open beyond one fixture family. [Thread review](../reviews/NavigationThreadReview.md) +pauses the fixture and proposes an independently developed application source contrast. +No new ownership pattern or canonical dimension is admitted. diff --git a/research/reviews/NavigationCoverage.md b/research/reviews/NavigationCoverage.md index ebf7106..d886c0d 100644 --- a/research/reviews/NavigationCoverage.md +++ b/research/reviews/NavigationCoverage.md @@ -40,3 +40,11 @@ accessibility work remains open. No maturity or M1/M2 promotion. the provisional VC-03 subset. Explicit focus/title/status policy and delayed-response ownership exercised; AT speech and full accessibility validation remain pending. This is a new stream-F comparison group, not another comparable cache cycle. + +## Ownership comparison and thread pause — 2026-09-27 + +[NO01–NO06](NavigationOwnershipExperiment.md) adds ordered competing-completion +evidence to provisional VC-03. Guarded policy meets 12/12 full obligations, prior +policy 0/12; one authored family, no AT/independent validation. The +[thread review](NavigationThreadReview.md) pauses local fixture expansion and names +a pinned Zulip navigation/search source contrast as the next evidence direction. diff --git a/research/reviews/NavigationOwnershipExperiment.md b/research/reviews/NavigationOwnershipExperiment.md new file mode 100644 index 0000000..43c64b5 --- /dev/null +++ b/research/reviews/NavigationOwnershipExperiment.md @@ -0,0 +1,95 @@ +# Competing navigation completion ownership — 2026-09-27 + +LEARN-0013, Codex author-model review. [Preregistration](../evolution/cycles/2026-09-27-navigation-ownership.md) +committed as `e484cae` before implementation/execution. Stream F; new group +F-navigation-ownership-v1. [Runner](../experiments/navigation-history/ownership.mjs), +[guarded policy](../experiments/navigation-history/ownership-policy.js), +[final receipt](../experiments/navigation-history/ownership-result.json). + +## Comparison and findings + +Same locked htmx 2.0.11/Playwright 1.63.0 and Chromium 151.0.7922.34/Firefox 155.0. +The unchanged previous focus-policy.js is the baseline, on a new fixture with stable +content target and persistent navigation links. Different controls can have concurrent +requests; there is no hx-sync configuration. Authored ownership policy is a separate +variant, not a vendor patch. Exact input/browser hashes and completion traces retained. + +All 24 rows completed in the corrected run. Baseline met all obligations in **0/12** +rows; guard met them in **12/12**. Baseline recording success is not obligation success. +These are six deliberately challenging scenarios in two browsers, not an application +quality score, independent trials or proof of all possible schedules. + +| Claim | Case | Baseline in both browsers | Guarded result in both browsers | +| --- | --- | --- | --- | +| NO01 | O01 delayed history, newer search focus | Restores alpha and preserves typed text but moves focus from search to heading | Restores alpha while retaining search focus/text | +| NO02 | O02 delayed history, newer gamma navigation | Old alpha replaces content/title/status under gamma's URL | Gamma content/URL/title/status/focus unchanged | +| NO03 | O03 older success after newer success | Old beta replaces gamma and pushes beta's URL | Gamma result retained | +| NO04 | O04 older failure after newer success | Gamma remains but old failure replaces its success feedback | Gamma feedback retained | +| NO05 | O05 newer request fails, older success follows | Old beta resurfaces, replacing committed alpha and newest failure feedback | Alpha and newest failure retained | +| NO06 | O06 browser Back supersedes old request | Old beta replaces restored alpha and pushes beta's URL | Alpha history result retained | + +O05 encodes an explicit latest-intent policy: a failed newer navigation does not revive +older intent without a new user decision. This is not a universal rule for every queue +or application. O01 distinguishes permission to restore content from permission to +move focus. During a pending Back miss, the browser URL already names alpha while beta +is still displayed; we measured completion obligations, not a complete pending-state +presentation/recovery design. No pending-URL consistency guarantee is established. + +## Mechanism and implementation limits + +Guarded navigation requests receive increasing generations; browser popstate advances +the same generation. Stale request completions are rejected at beforeOnLoad before +response processing. This covers success and error feedback, not just the content swap. +Focus checks remain separate from generation. History cache misses capture navigation +generation and a focus-change epoch. A still-current restoration can change the article +without moving focus after the user has started work in a surviving control. + +The history adapter wraps the XHR onload handler exposed by historyCacheMiss and checks +the generation and target path before calling the original. The public +[htmx event reference](https://htmx.org/events/) documents request cancellation and +modifiable history-request details; **wrapping the preinstalled handler is a pinned +implementation-dependent experiment**, not a portable supported router API. Inspected +htmx 2.0.11 loadHistoryFromServer sets the handler before emitting historyCacheMiss; +its historyCacheMissLoad notification alone does not veto the subsequent swap. No +claim that this implementation will remain valid across versions or extensions. + +No requests are aborted by this policy. Server traces show responses released and +client load events observed even when effects are discarded. Ignoring a completion is +not cancellation of server work, rollback, freshness verification or remote-write safety. +The fixture has no writes. A route generation is scoped to this one navigation surface, +not a global rule to suppress unrelated background work. + +## Retained harness correction + +[Initial receipt](../experiments/navigation-history/ownership-initial-result.json) +and [initial runner](../experiments/navigation-history/ownership-initial.mjs) preserve +20 completed rows and four Firefox harness failures. The forward-only Tab helper could +not return from the programmatically focused heading to earlier navigation controls in +O02/O06, in either policy. It did not execute those intended interleavings. The revised +helper chooses Shift+Tab when the target precedes the active element. No programmatic +focus, click, policy or assertion change was introduced. All 24 rows were rerun; initial +observations are not pooled as independent evidence. The initial runner file's hash +matches the initial receipt's ownership.mjs hash despite the archival filename. + +## Learning disposition: revision + +LEARN-0012 established only single-response continuation and explicitly left history/ +request races unresolved. This follow-up does not retract those bounded results. +It does revise the language's operational guidance: checking focus at swap time is +insufficient as an overall completion policy. Distinguish navigation ownership from +focus ownership; apply navigation checks to history, titles, URL and feedback, including +late errors and the case where the newest request fails. Record one consequential +revision across affected candidates, not twelve discoveries or a new pattern. + +URL-addressable State, Progressive Enhancement and the Focus Return scope boundary +remain hypotheses. Full-pattern recurrence, AT/human validation, repeated production +use, redirects, unload, nonzero swap/view-transition delays, multiple simultaneous +history misses, bfcache, pending-state disclosure and full lifecycle cleanup remain +unverified. The script has document-lifetime handlers; no mount/unmount reuse claim. + +[Measured effort](../evolution/cycles/2026-09-27-navigation-ownership-effort.json) +includes the initial run and corrected run as execution. Active time includes protocol, +implementation, analysis and recording; final administration excluded. No token/cost +estimate or diminishing-returns claim. See the [navigation-thread review](NavigationThreadReview.md) +for the agreed pause and next evidence direction. Residuals remain WP-0003 T01/T02; +measurement work remains WP-0006 T03. No further fixture-hardening cycle is started. diff --git a/research/reviews/NavigationThreadReview.md b/research/reviews/NavigationThreadReview.md new file mode 100644 index 0000000..a843dc3 --- /dev/null +++ b/research/reviews/NavigationThreadReview.md @@ -0,0 +1,59 @@ +# Navigation research thread review — 2026-09-27 + +Decision: pause expansion of the authored navigation fixture after LEARN-0013. +The user authorized a bounded ownership comparison followed by this review; completing +it does not close M1/M2, VC-03, the human-validation gate or the pattern language. + +| Cycle | Contribution | Learning disposition | +| --- | --- | --- | +| [LEARN-0011](NavigationHistoryExperiment.md) | Reproduced direct/native/history behavior and fragment-as-document reuse without Vary; separated history snapshots from HTTP cache | Corroboration | +| [LEARN-0012](NavigationFocusExperiment.md) | Distinguished native operation, same-ID focus retention, route continuation and feedback; preserved newer search focus for a single response | Corroboration | +| [LEARN-0013](NavigationOwnershipExperiment.md) | Exposed old history/success/error effects after newer intent; separated completion ownership from focus ownership, including newer failure | Revision | + +We now have executable support for a more precise public read-only navigation contract. +The third cycle changes operational guidance in existing candidates. It does not justify +another pattern solely because the fixture now has a generation counter. The modal +Focus Return candidate stays narrow; route continuation is a contrasting context. + +## What these results do not establish + +All three cycles share one authored htmx fixture family and one author-model reviewer. +Browser diversity strengthens the bounded mechanism observations; it does not supply +independent application recurrence. Screen-reader speech/usability, offline availability, +real intermediaries and deployed lifecycle behavior remain unverified. No fresh decision +usefulness evaluation was conducted; the frozen pilot's 18/24-versus-18/24 adjudication +and pending independent-human gate remain unchanged. + +Effort is recorded prospectively in each cycle's receipt. The protocols address different +question scopes: B-navigation-mechanism-v1, F-navigation-focus-v1 and +F-navigation-ownership-v1. They are **not three comparable observations of a learning +curve**. Do not infer acceleration, diminishing returns or a plateau from their rates. +Two corroborations followed by a revision are an account of this investigation, not a +productivity ranking. Harness correction effort remains visible rather than erased. + +## Why pause, and what follows + +Further fixture work could improve this adapter while leaving the main evidence gap +unchanged: whether the candidate contracts recur in independently developed applications. +Our next bounded direction is a **pinned, read-only Zulip navigation/search source +walkthrough** under WP-0003 T01. Ask where that application accepts, supersedes or +reports asynchronous navigation/search results, and how selection and focus ownership +are separated—or whether the proposed model does not fit. Reuse the existing provenance +workflow, pin the chosen revision, and cite exact mechanisms and counterexamples. +Do not count Zulip's previously inspected draft/send paths as new navigation evidence. +Static inspection would remain static evidence, not proof of deployed runtime behavior. +Register the question before execution; no additional model calls or study starts here. + +Keep these residuals live, without automatically extending the fixture: + +- WP-0003 T01: pending Back-state disclosure/recovery, multiple history misses, + redirects, nonzero swap delays, bfcache and lifecycle teardown before any production + recommendation; interrupted and late-script enhancement remains untested. +- WP-0003 T02: independent human/assistive-technology review and the offline-reading + branch of provisional VC-03. DOM live-region checks do not close these obligations. +- WP-0006 T03: reconcile remaining substantive-review dates and assess returns only + when useful comparable cycles exist. Do not manufacture cycles to satisfy a count. + +Reopen the local fixture when independent evidence exposes a specific contradiction +or a concrete consumer needs one of these limits resolved. The next source contrast +can narrow, merge or reject our claims; promotion is not its default outcome. diff --git a/research/walkthroughs/VC03-ContentPublication.md b/research/walkthroughs/VC03-ContentPublication.md index 5669bb7..ece3ed4 100644 --- a/research/walkthroughs/VC03-ContentPublication.md +++ b/research/walkthroughs/VC03-ContentPublication.md @@ -117,3 +117,14 @@ to persistent search. The managed policy passes its DOM obligations; raw same-ID focus retention does not itself update title or report errors in this fragment host. VC-03 stays provisional. Spoken feedback, focus visibility, delayed history/request races and offline availability remain open; no complete accessibility claim. + +## Competing navigation and bounded pause — 2026-09-27 + +[NO01–NO06](../reviews/NavigationOwnershipExperiment.md) show why result ownership +applies to content, URL, title and feedback separately from focus ownership. Old +history may overwrite newer content under a different URL; an old error may overwrite +newer success feedback. The guarded latest-intent policy handles the tested schedules, +including preserving committed content when the newest request fails. It is not a +general router or AT validation. Pending Back-state disclosure and broader history +lifecycle remain open. [Thread review](../reviews/NavigationThreadReview.md) pauses +fixture expansion; VC-03 stays provisional with independent/AT and offline gaps. diff --git a/workplans/FEP-WP-0003-contrasting-schools.md b/workplans/FEP-WP-0003-contrasting-schools.md index 473c976..b746c1b 100644 --- a/workplans/FEP-WP-0003-contrasting-schools.md +++ b/workplans/FEP-WP-0003-contrasting-schools.md @@ -215,3 +215,21 @@ WP-0003 T01 next: delayed history restoration and competing-navigation ownership interrupted/late enhancement remains open. T02 retains AT/independent review and offline branch. WP-0006 T03 remains progress for comparable windows and remaining review-date reconciliation. No M1/M2 completion. + +## Completion ownership compared; navigation fixture paused — 2026-09-27 + +LEARN-0013 preregistered at e484cae; 24 completed rows after a retained keyboard-helper +correction. Prior policy meets full obligations in 0/12, authored guard 12/12. Record +one revision: check navigation completion ownership separately from focus permission, +including history, errors and newer-request failure. Same authored family; hypotheses +and provisional VC-03 retained. See research/reviews/NavigationOwnershipExperiment.md +and NavigationThreadReview.md. Three distinct cycle groups do not establish a trend. + +Agreed pause: do not automatically extend the navigation fixture. Next bounded +WP-0003 T01 question is a preregistered, pinned read-only Zulip navigation/search +source contrast: locate selection/result/focus ownership or counterexamples outside +the authored fixture; prior draft/send evidence does not count as navigation support. +T01 retains pending history disclosure, multiple misses, redirects, delayed swaps, +bfcache/lifecycle and interrupted/late enhancement before any production claim. +T02 retains independent human/AT review and offline VC-03 branch. WP-0006 T03 retains +review-date reconciliation and useful comparable-cycle windows; no M1/M2 closure. diff --git a/workplans/FEP-WP-0006-language-evolution.md b/workplans/FEP-WP-0006-language-evolution.md index 924bdb7..b0fd4f9 100644 --- a/workplans/FEP-WP-0006-language-evolution.md +++ b/workplans/FEP-WP-0006-language-evolution.md @@ -94,3 +94,21 @@ WP-0003 T01 next: delayed history restoration and competing-navigation ownership interrupted/late enhancement remains open. T02 retains AT/independent review and offline branch. WP-0006 T03 remains progress for comparable windows and remaining review-date reconciliation. No M1/M2 completion. + +## Completion ownership compared; navigation fixture paused — 2026-09-27 + +LEARN-0013 preregistered at e484cae; 24 completed rows after a retained keyboard-helper +correction. Prior policy meets full obligations in 0/12, authored guard 12/12. Record +one revision: check navigation completion ownership separately from focus permission, +including history, errors and newer-request failure. Same authored family; hypotheses +and provisional VC-03 retained. See research/reviews/NavigationOwnershipExperiment.md +and NavigationThreadReview.md. Three distinct cycle groups do not establish a trend. + +Agreed pause: do not automatically extend the navigation fixture. Next bounded +WP-0003 T01 question is a preregistered, pinned read-only Zulip navigation/search +source contrast: locate selection/result/focus ownership or counterexamples outside +the authored fixture; prior draft/send evidence does not count as navigation support. +T01 retains pending history disclosure, multiple misses, redirects, delayed swaps, +bfcache/lifecycle and interrupted/late enhancement before any production claim. +T02 retains independent human/AT review and offline VC-03 branch. WP-0006 T03 retains +review-date reconciliation and useful comparable-cycle windows; no M1/M2 closure.