diff --git a/TREE.md b/TREE.md index ea4dd26..2c506e6 100644 --- a/TREE.md +++ b/TREE.md @@ -351,6 +351,8 @@ research/evolution/EvolutionReport.md research/evolution/README.md 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.md research/evolution/ledger.json research/evolution/profiles.json @@ -400,6 +402,9 @@ research/experiments/matrix-recovery/provenance.json research/experiments/matrix-recovery/result.json research/experiments/navigation-history/README.md research/experiments/navigation-history/experiment.mjs +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/package-lock.json research/experiments/navigation-history/package.json @@ -423,6 +428,7 @@ research/reviews/LocalFirstAdmissionScreen.json research/reviews/LocalFirstEvidenceExpansion.md research/reviews/MatrixRecoveryExperiment.md research/reviews/NavigationCoverage.md +research/reviews/NavigationFocusExperiment.md research/reviews/NavigationHistoryExperiment.md research/reviews/NavigationRenderingEvidence.md research/reviews/NestedDismissalIsolation.md diff --git a/research/candidates/FocusReturn.md b/research/candidates/FocusReturn.md index 910b1c8..875de54 100644 --- a/research/candidates/FocusReturn.md +++ b/research/candidates/FocusReturn.md @@ -74,3 +74,14 @@ requires application policy. This supports separating input ownership from focus restoration, not a general production-ready fix: animated exits, competing owners, rapid input bursts and assistive technology remain untested. Sixteen hypotheses; no new pattern or maturity promotion. + +## Boundary with route continuation — 2026-09-27 + +[Navigation focus contrast](../reviews/NavigationFocusExperiment.md) NF02–NF06 +provides a non-modal counter-context: a successful route may continue at a new heading, +while failed navigation keeps the invoker and background work leaves focus alone. +A held response must respect focus moved to a surviving search field. Raw htmx in +this fixture restores a same-ID link; that behavior alone does not choose the next +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. diff --git a/research/candidates/ProgressiveEnhancement.md b/research/candidates/ProgressiveEnhancement.md index 64e4add..54dc834 100644 --- a/research/candidates/ProgressiveEnhancement.md +++ b/research/candidates/ProgressiveEnhancement.md @@ -50,3 +50,18 @@ native recovery link navigates successfully. This does not establish automatic f late-script safety, accessible focus/announcements or offline availability. See [NH01–NH06](../reviews/NavigationHistoryExperiment.md). One authored application with a real package, not independent deployed recurrence. Hypothesis retained. + +## Keyboard continuation and feedback — 2026-09-27 + +[Navigation focus experiment](../reviews/NavigationFocusExperiment.md) NF01–NF06 +distinguishes successful route continuation, failed navigation, history restoration +and background updates. In the raw fixture, a replacement link retains focus but +fragment navigation leaves the old title and failures produce no visible status. +The authored policy updates title/status, selects a heading for direct continuation +and preserves a surviving control when the user has moved focus during the request. + +Choose a continuation policy for the task; do not universally focus a heading or +return to the invoker. Check ownership when the response is applied, not only when +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. diff --git a/research/catalog/SourceCatalog.md b/research/catalog/SourceCatalog.md index c9d35b5..fca224d 100644 --- a/research/catalog/SourceCatalog.md +++ b/research/catalog/SourceCatalog.md @@ -173,3 +173,19 @@ binaries. Authored HTTP application; 18 browser cases plus explicit HTTP validat sequence repeated twice. [NH01–NH06 review](../reviews/NavigationHistoryExperiment.md), preregistered prospective corroboration. This is one implementation family, not an independent production deployment. Initial launch failure receipt preserved. + +## Navigation focus and feedback — 2026-09-27 + +SRC-0038 follow-up: [NF01–NF06](../reviews/NavigationFocusExperiment.md), separate +focus runner and authored policy using the same pinned htmx fixture family. 22 +registered keyboard/DOM rows; not assistive-technology validation or independent use. + +SRC-0039: [W3C Understanding Focus Order](https://www.w3.org/WAI/WCAG22/Understanding/focus-order.html) +and [Status Messages](https://www.w3.org/WAI/WCAG22/Understanding/status-messages.html), +primary interpretive guidance accessed 2026-09-27; living revision unknown. One W3C +family, not two application uses. NF review limits the inference to meaningful focus +order and programmatically exposed feedback; no conformance claim from this fixture. + +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. diff --git a/research/evolution/EvolutionReport.md b/research/evolution/EvolutionReport.md index 545d758..d301334 100644 --- a/research/evolution/EvolutionReport.md +++ b/research/evolution/EvolutionReport.md @@ -22,7 +22,7 @@ Independent human profile reviews: 0/17. Full-contract independent-use counts un ## Change since baseline -Baseline baseline-2026-09-26; source revision `f273a190f0e08b8cfe0bb7260d0e7d889f1c090a`. Added: 0; removed: 0; shared candidates with changed content: 2; changed evidence/review assessments: 2; maturity transitions: 0. +Baseline baseline-2026-09-26; source revision `f273a190f0e08b8cfe0bb7260d0e7d889f1c090a`. Added: 0; removed: 0; shared candidates with changed content: 3; changed evidence/review assessments: 3; maturity transitions: 0. The shared cohort is compared separately from additions. Changed text or evidence counts are not learning gains. @@ -51,13 +51,13 @@ Ten selected retrospective outcomes (not a historical census), followed by prosp | Outcome | Entries (not independent discoveries) | | --- | ---: | -| corroboration | 1 | +| corroboration | 2 | | inconclusive | 0 | | merge | 0 | | rejection | 0 | | revision | 10 | -Cycles with recorded research effort: 1/11. Unknown effort is neither zero nor estimated from commits, tokens or assertion counts. +Cycles with recorded research effort: 2/12. Unknown effort is neither zero nor estimated from commits, tokens or assertion counts. ### Stream B / B-navigation-mechanism-v1 @@ -67,11 +67,19 @@ Cycles with recorded research effort: 1/11. Unknown effort is neither zero nor e 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-focus-v1 + +| Cycle | Outcome | Research minutes | Consequential outcome/hour | +| --- | --- | ---: | ---: | +| LEARN-0012 | corroboration | 5.138 | 0.00 | + +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. -Profiles with unknown last substantive review date: 15. Assessment dates record this metadata review, not a new substantive validation. No age-based freshness claim is available for those profiles. +Profiles with unknown last substantive review date: 14. Assessment dates record this metadata review, not a new substantive validation. No age-based freshness claim is available for those profiles. Coherence metrics (ambiguous selections, unjustified relations and overlap resolved): not measured. Conflict Copy / Explicit Reconciliation overlap remains an explicit open question. @@ -230,8 +238,9 @@ Keep essential actions usable when enhancement is unavailable. | documentary | htmx | Boosted links and forms. | [NavigationRenderingEvidence](../../research/reviews/NavigationRenderingEvidence.md) | | 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) | -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 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. Superseded by: none recorded. @@ -318,8 +327,9 @@ Restore meaningful focus after a temporary interaction ends. | documentary | WAI-ARIA APG | Dialog focus obligations. | [PatternBreadthExpansion](../../research/reviews/PatternBreadthExpansion.md) | | 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) | -Open limits: One implementation family; repeated trials are not independent uses. Animated exits and competing owners remain untested; no production repair claim. +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. Superseded by: none recorded. @@ -495,3 +505,17 @@ Evidence: [NavigationHistoryExperiment](../../research/reviews/NavigationHistory Remaining uncertainty: One authored application, no independent recurrence or usefulness gain. Accessibility, delayed scripts, interrupted transport, real intermediaries and offline availability remain open. Supersedes: none recorded. + +### LEARN-0012 — Does enhanced route replacement need explicit continuation and feedback policy that respects changed focus ownership? + +2026-09-27 · stream F · prospective · corroboration + +Before: Native controls alone do not establish enhanced focus/feedback semantics; delayed responses should respect focus moved to another surviving task. Modal return is not automatically route continuation. + +Conclusion: Corroborated in 22 cases: raw same-ID link focus survives but this fragment contract leaves stale title and silent failure; authored heading/status policy works and preserves newer search focus. No AT speech or universal heading policy established. + +Evidence: [NavigationFocusExperiment](../../research/reviews/NavigationFocusExperiment.md), [focus-result](../../research/experiments/navigation-history/focus-result.json), [2026-09-27-navigation-focus-effort](../../research/evolution/cycles/2026-09-27-navigation-focus-effort.json). + +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. diff --git a/research/evolution/cycles/2026-09-27-navigation-focus-effort.json b/research/evolution/cycles/2026-09-27-navigation-focus-effort.json new file mode 100644 index 0000000..beadc2b --- /dev/null +++ b/research/evolution/cycles/2026-09-27-navigation-focus-effort.json @@ -0,0 +1,34 @@ +{ + "start": 198900.113504947, + "execution_seconds": 31.501304913981585, + "executions": [ + { + "command": [ + "node", + "research/experiments/navigation-history/focus.mjs" + ], + "seconds": 17.86536105797859, + "exit_code": 0 + }, + { + "command": [ + "make", + "evolution-check", + "evolution-test", + "schema-test", + "bench-test" + ], + "seconds": 13.635943856002996, + "exit_code": 0 + } + ], + "status": "complete", + "start_utc_approx": "2026-09-26T22:16:12.694172+00:00", + "end_utc": "2026-09-26T22:21:52.471986+00:00", + "elapsed_seconds": 339.778, + "research_minutes": 5.138, + "execution_minutes": 0.525, + "method": "Single-agent monotonic elapsed session minus blocking experiment/check subprocess durations. Checkpoint inspection assigned to execution; no double count. Rounded minute fields are elapsed agent effort, not human labor or model compute.", + "excluded": "Orientation before clock; final receipt refresh, commit, snapshot and State Hub sync after cutoff. Tokens and cost unknown.", + "budget": "Within preregistered 45 active research / 10 execution minute budget." +} diff --git a/research/evolution/ledger.json b/research/evolution/ledger.json index fa33cfa..f4e41a4 100644 --- a/research/evolution/ledger.json +++ b/research/evolution/ledger.json @@ -373,6 +373,43 @@ "cost_usd": null }, "supersedes": [] + }, + { + "id": "LEARN-0012", + "date": "2026-09-27", + "recording": "prospective", + "stream": "F", + "comparison_group": "F-navigation-focus-v1", + "protocol_ref": "research/evolution/cycles/2026-09-27-navigation-focus.md", + "question": "Does enhanced route replacement need explicit continuation and feedback policy that respects changed focus ownership?", + "previous_belief": "Native controls alone do not establish enhanced focus/feedback semantics; delayed responses should respect focus moved to another surviving task. Modal return is not automatically route continuation.", + "conclusion": "Corroborated in 22 cases: raw same-ID link focus survives but this fragment contract leaves stale title and silent failure; authored heading/status policy works and preserves newer search focus. No AT speech or universal heading policy established.", + "disposition": "corroboration", + "affected_patterns": [ + "CAND-0010", + "CAND-0016" + ], + "evidence": [ + "research/reviews/NavigationFocusExperiment.md", + "research/experiments/navigation-history/focus-result.json", + "research/evolution/cycles/2026-09-27-navigation-focus-effort.json" + ], + "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." + ], + "reviewer": { + "name": "Codex", + "role": "author-model", + "independent_human": false + }, + "effort": { + "research_minutes": 5.138, + "execution_minutes": 0.525, + "reported_tokens": null, + "cost_usd": null + }, + "supersedes": [] } ] } diff --git a/research/evolution/profiles.json b/research/evolution/profiles.json index f56613d..7dcf782 100644 --- a/research/evolution/profiles.json +++ b/research/evolution/profiles.json @@ -359,7 +359,7 @@ "id": "CAND-0010", "path": "research/candidates/ProgressiveEnhancement.md", "title": "Progressive Enhancement", - "sha256": "e9ca2b046cb69fff16379e22e1a9d0d61d2a5801ff92bbfc1b56ae3e3be16c7c", + "sha256": "ec3ab16d680308593af4d22fbbcbea737fe46096c5f3e65968d0db3a8befec21", "status": "draft", "maturity": "hypothesis", "decision": "Keep essential actions usable when enhancement is unavailable.", @@ -381,6 +381,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/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." } ], "independent_full_contract_uses": null, @@ -392,8 +398,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 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." ], "superseded_by": [] }, @@ -560,7 +566,7 @@ "id": "CAND-0016", "path": "research/candidates/FocusReturn.md", "title": "Focus Return", - "sha256": "b44f976684d7f2a9186e09aaa6fd6fdb51dc5674a53bcc90afa08748a641f041", + "sha256": "129ef7623d11da91f3f5429686455be16180e5477d1706836f5c1c3617698eae", "status": "draft", "maturity": "hypothesis", "decision": "Restore meaningful focus after a temporary interaction ends.", @@ -582,19 +588,26 @@ "family": "Radix", "ref": "research/reviews/NestedOwnershipRouting.md", "scope": "Authored fixture: raw 20/24 versus routed 24/24; browser/build limits retained." + }, + { + "kind": "runtime", + "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." } ], "independent_full_contract_uses": null, "review": { - "assessed_at": "2026-09-26", + "assessed_at": "2026-09-27", "reviewer": "Codex", "role": "author-model", "independent_human": false, - "last_substantive_review_date": null + "last_substantive_review_date": "2026-09-27" }, "gaps": [ "One implementation family; repeated trials are not independent uses.", - "Animated exits and competing owners remain untested; no production repair claim." + "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." ], "superseded_by": [] }, diff --git a/research/experiments/navigation-history/README.md b/research/experiments/navigation-history/README.md index 30274c9..ecd828d 100644 --- a/research/experiments/navigation-history/README.md +++ b/research/experiments/navigation-history/README.md @@ -38,3 +38,18 @@ History eviction uses htmx 2.0.11's sessionStorage key, an experimental control to the pinned implementation. No assertion of identical content freshness across history, general offline availability, accessibility, script-late races, writes, real intermediary correctness or independent full-pattern recurrence follows. + +## Keyboard/focus follow-up + +[Preregistered LEARN-0012](../../evolution/cycles/2026-09-27-navigation-focus.md), +[review](../../reviews/NavigationFocusExperiment.md), [receipt](focus-result.json). +Run the same browser environment with `node focus.mjs`. Output defaults to +`/tmp/fep-focus-result.json`; choose a fresh `FEP_RESULT_PATH` per execution. +The separate focus runner/policy preserve the earlier navigation/cache inputs and +receipt hashes. Same locked packages. 22 cases: native keyboard plus raw/managed +navigation, 503 feedback, held-response focus ownership, history and background work. + +Raw rows observe behavior, so 22 passing rows do not mean raw and managed variants +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. diff --git a/research/experiments/navigation-history/focus-policy.js b/research/experiments/navigation-history/focus-policy.js new file mode 100644 index 0000000..f358b0a --- /dev/null +++ b/research/experiments/navigation-history/focus-policy.js @@ -0,0 +1,32 @@ +// Authored experimental navigation policy; raw variant installs only diagnostics. +window.probe=[]; +const managed=new URL(location.href).searchParams.get('mode')==='managed'; +const pending=new WeakMap(); +const notice=message=>{document.querySelector('#feedback').textContent=message;}; +function complete(moveFocus){ + const heading=document.querySelector('#heading'); + document.title=`${heading.textContent} — Catalog`; + notice(`Loaded ${heading.textContent}`); + if(moveFocus)heading.focus(); +} +document.addEventListener('htmx:beforeRequest',e=>{ + if(managed&&e.detail.target.id==='content')pending.set(e.detail.xhr,{source:e.detail.elt,ownsFocus:false}); +}); +document.addEventListener('htmx:beforeSwap',e=>{ + const state=pending.get(e.detail.xhr); + if(state)state.ownsFocus=document.activeElement===state.source; +}); +document.addEventListener('htmx:afterSwap',e=>{ + const state=pending.get(e.detail.xhr); + if(state){complete(state.ownsFocus);pending.delete(e.detail.xhr);} +}); +document.addEventListener('htmx:responseError',e=>{ + if(managed&&pending.has(e.detail.xhr)){ + notice('Navigation failed. Current article retained. Use Native recovery to continue.'); + pending.delete(e.detail.xhr); + } +}); +document.addEventListener('htmx:historyRestore',()=>{if(managed)complete(true);}); +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/focus-result.json b/research/experiments/navigation-history/focus-result.json new file mode 100644 index 0000000..28ebe12 --- /dev/null +++ b/research/experiments/navigation-history/focus-result.json @@ -0,0 +1,1354 @@ +{ + "date": "2026-09-26T22:19:11.590Z", + "node": "v24.11.1", + "kind": "keyboard-focus-dom-observation-not-assistive-technology-validation", + "files": { + "focus.mjs": "74b0a2f15f761beadfb5945f0d5637493198711ea9f7332f47a37f75ac00cc4c", + "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": "K01-native-keyboard", + "engine": "chromium", + "mode": "native", + "status": "pass", + "observation": { + "keyboardLink": true + }, + "final": { + "focus": "BODY", + "title": "beta — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=raw", + "heading": "beta", + "feedback": "", + "statusRole": "status", + "live": "polite" + }, + "events": [], + "requests": [ + { + "path": "/articles?topic=alpha&mode=native", + "hx": false, + "restore": false + }, + { + "path": "/favicon.ico", + "hx": false, + "restore": false + }, + { + "path": "/articles?topic=beta&mode=raw", + "hx": false, + "restore": false + } + ] + }, + { + "id": "K02-enhanced-keyboard", + "engine": "chromium", + "mode": "raw", + "status": "pass", + "observation": { + "focus": "next", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=raw", + "heading": "beta", + "feedback": "", + "statusRole": "status", + "live": "polite", + "headingContinuation": false, + "updatedTitle": false, + "completionFeedback": false + }, + "final": { + "focus": "next", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=raw", + "heading": "beta", + "feedback": "", + "statusRole": "status", + "live": "polite" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "next", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=raw", + "feedback": "" + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=raw", + "hx": false, + "restore": false + }, + { + "path": "/policy.js", + "hx": false, + "restore": false + }, + { + "path": "/htmx.js", + "hx": false, + "restore": false + }, + { + "path": "/articles?topic=beta&mode=raw", + "hx": true, + "restore": false + } + ] + }, + { + "id": "K03-error-feedback-recovery", + "engine": "chromium", + "mode": "raw", + "status": "pass", + "observation": { + "focus": "broken", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=raw", + "heading": "alpha", + "feedback": "", + "statusRole": "status", + "live": "polite", + "failureFeedback": false, + "nativeRecovery": true + }, + "final": { + "focus": "BODY", + "title": "beta — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=raw&fail=1", + "heading": "beta", + "feedback": "", + "statusRole": "status", + "live": "polite" + }, + "events": [], + "requests": [ + { + "path": "/articles?topic=alpha&mode=raw", + "hx": false, + "restore": false + }, + { + "path": "/policy.js", + "hx": false, + "restore": false + }, + { + "path": "/htmx.js", + "hx": false, + "restore": false + }, + { + "path": "/articles?topic=beta&mode=raw&fail=1", + "hx": true, + "restore": false + }, + { + "path": "/articles?topic=beta&mode=raw&fail=1", + "hx": false, + "restore": false + }, + { + "path": "/policy.js", + "hx": false, + "restore": false + }, + { + "path": "/htmx.js", + "hx": false, + "restore": false + } + ] + }, + { + "id": "K04-delayed-new-focus", + "engine": "chromium", + "mode": "raw", + "status": "pass", + "observation": { + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=raw&slow=1", + "heading": "beta", + "feedback": "", + "statusRole": "status", + "live": "polite" + }, + "final": { + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=raw&slow=1", + "heading": "beta", + "feedback": "", + "statusRole": "status", + "live": "polite" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=raw&slow=1", + "feedback": "" + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=raw", + "hx": false, + "restore": false + }, + { + "path": "/policy.js", + "hx": false, + "restore": false + }, + { + "path": "/htmx.js", + "hx": false, + "restore": false + }, + { + "path": "/articles?topic=beta&mode=raw&slow=1", + "hx": true, + "restore": false + } + ] + }, + { + "id": "K05-history-continuation", + "engine": "chromium", + "mode": "raw", + "status": "pass", + "observation": { + "focus": "next", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=raw", + "heading": "alpha", + "feedback": "", + "statusRole": "status", + "live": "polite" + }, + "final": { + "focus": "next", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=raw", + "heading": "alpha", + "feedback": "", + "statusRole": "status", + "live": "polite" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "next", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=raw", + "feedback": "" + }, + { + "name": "htmx:afterSettle", + "focus": "next", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=raw", + "feedback": "" + }, + { + "name": "htmx:historyRestore", + "focus": "next", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=raw", + "feedback": "" + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=raw", + "hx": false, + "restore": false + }, + { + "path": "/policy.js", + "hx": false, + "restore": false + }, + { + "path": "/htmx.js", + "hx": false, + "restore": false + }, + { + "path": "/articles?topic=beta&mode=raw", + "hx": true, + "restore": false + } + ] + }, + { + "id": "K06-background-no-theft", + "engine": "chromium", + "mode": "raw", + "status": "pass", + "observation": { + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=raw", + "heading": "alpha", + "feedback": "", + "statusRole": "status", + "live": "polite" + }, + "final": { + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=raw", + "heading": "alpha", + "feedback": "", + "statusRole": "status", + "live": "polite" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=raw", + "feedback": "" + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=raw", + "hx": false, + "restore": false + }, + { + "path": "/policy.js", + "hx": false, + "restore": false + }, + { + "path": "/htmx.js", + "hx": false, + "restore": false + }, + { + "path": "/news", + "hx": true, + "restore": false + } + ] + }, + { + "id": "K02-enhanced-keyboard", + "engine": "chromium", + "mode": "managed", + "status": "pass", + "observation": { + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=managed", + "heading": "beta", + "feedback": "Loaded beta", + "statusRole": "status", + "live": "polite", + "headingContinuation": true, + "updatedTitle": true, + "completionFeedback": true + }, + "final": { + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=managed", + "heading": "beta", + "feedback": "Loaded beta", + "statusRole": "status", + "live": "polite" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=managed", + "feedback": "Loaded beta" + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "restore": false + }, + { + "path": "/policy.js", + "hx": false, + "restore": false + }, + { + "path": "/htmx.js", + "hx": false, + "restore": false + }, + { + "path": "/articles?topic=beta&mode=managed", + "hx": true, + "restore": false + } + ] + }, + { + "id": "K03-error-feedback-recovery", + "engine": "chromium", + "mode": "managed", + "status": "pass", + "observation": { + "focus": "broken", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=managed", + "heading": "alpha", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue.", + "statusRole": "status", + "live": "polite", + "failureFeedback": true, + "nativeRecovery": true + }, + "final": { + "focus": "BODY", + "title": "beta — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=managed&fail=1", + "heading": "beta", + "feedback": "", + "statusRole": "status", + "live": "polite" + }, + "events": [], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "restore": false + }, + { + "path": "/policy.js", + "hx": false, + "restore": false + }, + { + "path": "/htmx.js", + "hx": false, + "restore": false + }, + { + "path": "/articles?topic=beta&mode=managed&fail=1", + "hx": true, + "restore": false + }, + { + "path": "/articles?topic=beta&mode=managed&fail=1", + "hx": false, + "restore": false + }, + { + "path": "/policy.js", + "hx": false, + "restore": false + }, + { + "path": "/htmx.js", + "hx": false, + "restore": false + } + ] + }, + { + "id": "K04-delayed-new-focus", + "engine": "chromium", + "mode": "managed", + "status": "pass", + "observation": { + "focus": "search", + "title": "beta — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=managed&slow=1", + "heading": "beta", + "feedback": "Loaded beta", + "statusRole": "status", + "live": "polite" + }, + "final": { + "focus": "search", + "title": "beta — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=managed&slow=1", + "heading": "beta", + "feedback": "Loaded beta", + "statusRole": "status", + "live": "polite" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "search", + "title": "beta — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=managed&slow=1", + "feedback": "Loaded beta" + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "restore": false + }, + { + "path": "/policy.js", + "hx": false, + "restore": false + }, + { + "path": "/htmx.js", + "hx": false, + "restore": false + }, + { + "path": "/articles?topic=beta&mode=managed&slow=1", + "hx": true, + "restore": false + } + ] + }, + { + "id": "K05-history-continuation", + "engine": "chromium", + "mode": "managed", + "status": "pass", + "observation": { + "focus": "heading", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=managed", + "heading": "alpha", + "feedback": "Loaded alpha", + "statusRole": "status", + "live": "polite" + }, + "final": { + "focus": "heading", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=managed", + "heading": "alpha", + "feedback": "Loaded alpha", + "statusRole": "status", + "live": "polite" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=managed", + "feedback": "Loaded beta" + }, + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=managed", + "feedback": "Loaded beta" + }, + { + "name": "htmx:historyRestore", + "focus": "heading", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=managed", + "feedback": "Loaded alpha" + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "restore": false + }, + { + "path": "/policy.js", + "hx": false, + "restore": false + }, + { + "path": "/htmx.js", + "hx": false, + "restore": false + }, + { + "path": "/articles?topic=beta&mode=managed", + "hx": true, + "restore": false + } + ] + }, + { + "id": "K06-background-no-theft", + "engine": "chromium", + "mode": "managed", + "status": "pass", + "observation": { + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=managed", + "heading": "alpha", + "feedback": "", + "statusRole": "status", + "live": "polite" + }, + "final": { + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=managed", + "heading": "alpha", + "feedback": "", + "statusRole": "status", + "live": "polite" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=managed", + "feedback": "" + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "restore": false + }, + { + "path": "/policy.js", + "hx": false, + "restore": false + }, + { + "path": "/htmx.js", + "hx": false, + "restore": false + }, + { + "path": "/news", + "hx": true, + "restore": false + } + ] + }, + { + "id": "K01-native-keyboard", + "engine": "firefox", + "mode": "native", + "status": "pass", + "observation": { + "keyboardLink": true + }, + "final": { + "focus": "BODY", + "title": "beta — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=raw", + "heading": "beta", + "feedback": "", + "statusRole": "status", + "live": "polite" + }, + "events": [], + "requests": [ + { + "path": "/articles?topic=alpha&mode=native", + "hx": false, + "restore": false + }, + { + "path": "/favicon.ico", + "hx": false, + "restore": false + }, + { + "path": "/articles?topic=beta&mode=raw", + "hx": false, + "restore": false + } + ] + }, + { + "id": "K02-enhanced-keyboard", + "engine": "firefox", + "mode": "raw", + "status": "pass", + "observation": { + "focus": "next", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=raw", + "heading": "beta", + "feedback": "", + "statusRole": "status", + "live": "polite", + "headingContinuation": false, + "updatedTitle": false, + "completionFeedback": false + }, + "final": { + "focus": "next", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=raw", + "heading": "beta", + "feedback": "", + "statusRole": "status", + "live": "polite" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "next", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=raw", + "feedback": "" + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=raw", + "hx": false, + "restore": false + }, + { + "path": "/policy.js", + "hx": false, + "restore": false + }, + { + "path": "/htmx.js", + "hx": false, + "restore": false + }, + { + "path": "/favicon.ico", + "hx": false, + "restore": false + }, + { + "path": "/articles?topic=beta&mode=raw", + "hx": true, + "restore": false + } + ] + }, + { + "id": "K03-error-feedback-recovery", + "engine": "firefox", + "mode": "raw", + "status": "pass", + "observation": { + "focus": "broken", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=raw", + "heading": "alpha", + "feedback": "", + "statusRole": "status", + "live": "polite", + "failureFeedback": false, + "nativeRecovery": true + }, + "final": { + "focus": "BODY", + "title": "beta — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=raw&fail=1", + "heading": "beta", + "feedback": "", + "statusRole": "status", + "live": "polite" + }, + "events": [], + "requests": [ + { + "path": "/articles?topic=alpha&mode=raw", + "hx": false, + "restore": false + }, + { + "path": "/policy.js", + "hx": false, + "restore": false + }, + { + "path": "/htmx.js", + "hx": false, + "restore": false + }, + { + "path": "/favicon.ico", + "hx": false, + "restore": false + }, + { + "path": "/articles?topic=beta&mode=raw&fail=1", + "hx": true, + "restore": false + }, + { + "path": "/articles?topic=beta&mode=raw&fail=1", + "hx": false, + "restore": false + }, + { + "path": "/policy.js", + "hx": false, + "restore": false + }, + { + "path": "/htmx.js", + "hx": false, + "restore": false + } + ] + }, + { + "id": "K04-delayed-new-focus", + "engine": "firefox", + "mode": "raw", + "status": "pass", + "observation": { + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=raw&slow=1", + "heading": "beta", + "feedback": "", + "statusRole": "status", + "live": "polite" + }, + "final": { + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=raw&slow=1", + "heading": "beta", + "feedback": "", + "statusRole": "status", + "live": "polite" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=raw&slow=1", + "feedback": "" + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=raw", + "hx": false, + "restore": false + }, + { + "path": "/policy.js", + "hx": false, + "restore": false + }, + { + "path": "/htmx.js", + "hx": false, + "restore": false + }, + { + "path": "/favicon.ico", + "hx": false, + "restore": false + }, + { + "path": "/articles?topic=beta&mode=raw&slow=1", + "hx": true, + "restore": false + } + ] + }, + { + "id": "K05-history-continuation", + "engine": "firefox", + "mode": "raw", + "status": "pass", + "observation": { + "focus": "next", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=raw", + "heading": "alpha", + "feedback": "", + "statusRole": "status", + "live": "polite" + }, + "final": { + "focus": "next", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=raw", + "heading": "alpha", + "feedback": "", + "statusRole": "status", + "live": "polite" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "next", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=raw", + "feedback": "" + }, + { + "name": "htmx:afterSettle", + "focus": "next", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=raw", + "feedback": "" + }, + { + "name": "htmx:historyRestore", + "focus": "next", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=raw", + "feedback": "" + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=raw", + "hx": false, + "restore": false + }, + { + "path": "/policy.js", + "hx": false, + "restore": false + }, + { + "path": "/htmx.js", + "hx": false, + "restore": false + }, + { + "path": "/favicon.ico", + "hx": false, + "restore": false + }, + { + "path": "/articles?topic=beta&mode=raw", + "hx": true, + "restore": false + } + ] + }, + { + "id": "K06-background-no-theft", + "engine": "firefox", + "mode": "raw", + "status": "pass", + "observation": { + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=raw", + "heading": "alpha", + "feedback": "", + "statusRole": "status", + "live": "polite" + }, + "final": { + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=raw", + "heading": "alpha", + "feedback": "", + "statusRole": "status", + "live": "polite" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=raw", + "feedback": "" + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=raw", + "hx": false, + "restore": false + }, + { + "path": "/policy.js", + "hx": false, + "restore": false + }, + { + "path": "/htmx.js", + "hx": false, + "restore": false + }, + { + "path": "/favicon.ico", + "hx": false, + "restore": false + }, + { + "path": "/news", + "hx": true, + "restore": false + } + ] + }, + { + "id": "K02-enhanced-keyboard", + "engine": "firefox", + "mode": "managed", + "status": "pass", + "observation": { + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=managed", + "heading": "beta", + "feedback": "Loaded beta", + "statusRole": "status", + "live": "polite", + "headingContinuation": true, + "updatedTitle": true, + "completionFeedback": true + }, + "final": { + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=managed", + "heading": "beta", + "feedback": "Loaded beta", + "statusRole": "status", + "live": "polite" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=managed", + "feedback": "Loaded beta" + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "restore": false + }, + { + "path": "/policy.js", + "hx": false, + "restore": false + }, + { + "path": "/htmx.js", + "hx": false, + "restore": false + }, + { + "path": "/favicon.ico", + "hx": false, + "restore": false + }, + { + "path": "/articles?topic=beta&mode=managed", + "hx": true, + "restore": false + } + ] + }, + { + "id": "K03-error-feedback-recovery", + "engine": "firefox", + "mode": "managed", + "status": "pass", + "observation": { + "focus": "broken", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=managed", + "heading": "alpha", + "feedback": "Navigation failed. Current article retained. Use Native recovery to continue.", + "statusRole": "status", + "live": "polite", + "failureFeedback": true, + "nativeRecovery": true + }, + "final": { + "focus": "BODY", + "title": "beta — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=managed&fail=1", + "heading": "beta", + "feedback": "", + "statusRole": "status", + "live": "polite" + }, + "events": [], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "restore": false + }, + { + "path": "/policy.js", + "hx": false, + "restore": false + }, + { + "path": "/htmx.js", + "hx": false, + "restore": false + }, + { + "path": "/favicon.ico", + "hx": false, + "restore": false + }, + { + "path": "/articles?topic=beta&mode=managed&fail=1", + "hx": true, + "restore": false + }, + { + "path": "/articles?topic=beta&mode=managed&fail=1", + "hx": false, + "restore": false + }, + { + "path": "/policy.js", + "hx": false, + "restore": false + }, + { + "path": "/htmx.js", + "hx": false, + "restore": false + } + ] + }, + { + "id": "K04-delayed-new-focus", + "engine": "firefox", + "mode": "managed", + "status": "pass", + "observation": { + "focus": "search", + "title": "beta — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=managed&slow=1", + "heading": "beta", + "feedback": "Loaded beta", + "statusRole": "status", + "live": "polite" + }, + "final": { + "focus": "search", + "title": "beta — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=managed&slow=1", + "heading": "beta", + "feedback": "Loaded beta", + "statusRole": "status", + "live": "polite" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "search", + "title": "beta — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=managed&slow=1", + "feedback": "Loaded beta" + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "restore": false + }, + { + "path": "/policy.js", + "hx": false, + "restore": false + }, + { + "path": "/htmx.js", + "hx": false, + "restore": false + }, + { + "path": "/favicon.ico", + "hx": false, + "restore": false + }, + { + "path": "/articles?topic=beta&mode=managed&slow=1", + "hx": true, + "restore": false + } + ] + }, + { + "id": "K05-history-continuation", + "engine": "firefox", + "mode": "managed", + "status": "pass", + "observation": { + "focus": "heading", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=managed", + "heading": "alpha", + "feedback": "Loaded alpha", + "statusRole": "status", + "live": "polite" + }, + "final": { + "focus": "heading", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=managed", + "heading": "alpha", + "feedback": "Loaded alpha", + "statusRole": "status", + "live": "polite" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "beta — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=beta&mode=managed", + "feedback": "Loaded beta" + }, + { + "name": "htmx:afterSettle", + "focus": "heading", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=managed", + "feedback": "Loaded beta" + }, + { + "name": "htmx:historyRestore", + "focus": "heading", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=managed", + "feedback": "Loaded alpha" + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "restore": false + }, + { + "path": "/policy.js", + "hx": false, + "restore": false + }, + { + "path": "/htmx.js", + "hx": false, + "restore": false + }, + { + "path": "/favicon.ico", + "hx": false, + "restore": false + }, + { + "path": "/articles?topic=beta&mode=managed", + "hx": true, + "restore": false + } + ] + }, + { + "id": "K06-background-no-theft", + "engine": "firefox", + "mode": "managed", + "status": "pass", + "observation": { + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=managed", + "heading": "alpha", + "feedback": "", + "statusRole": "status", + "live": "polite" + }, + "final": { + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=managed", + "heading": "alpha", + "feedback": "", + "statusRole": "status", + "live": "polite" + }, + "events": [ + { + "name": "htmx:afterSettle", + "focus": "search", + "title": "alpha — Catalog", + "url": "http://127.0.0.1:41981/articles?topic=alpha&mode=managed", + "feedback": "" + } + ], + "requests": [ + { + "path": "/articles?topic=alpha&mode=managed", + "hx": false, + "restore": false + }, + { + "path": "/policy.js", + "hx": false, + "restore": false + }, + { + "path": "/htmx.js", + "hx": false, + "restore": false + }, + { + "path": "/favicon.ico", + "hx": false, + "restore": false + }, + { + "path": "/news", + "hx": true, + "restore": false + } + ] + } + ], + "passed": 22, + "total": 22 +} diff --git a/research/experiments/navigation-history/focus.mjs b/research/experiments/navigation-history/focus.mjs new file mode 100644 index 0000000..3e92c9c --- /dev/null +++ b/research/experiments/navigation-history/focus.mjs @@ -0,0 +1,74 @@ +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 htmx=await readFile(new URL('node_modules/htmx.org/dist/htmx.js',dir)); +const policy=await readFile(new URL('focus-policy.js',dir)); +let requests=[],release=null,heldResolve=null; +const server=createServer((req,res)=>{ + const u=new URL(req.url,'http://local');res.setHeader('Cache-Control','no-store'); + const hx=req.headers['hx-request']==='true';requests.push({path:req.url,hx,restore:req.headers['hx-history-restore-request']==='true'}); + if(u.pathname==='/htmx.js'||u.pathname==='/policy.js'){res.setHeader('Content-Type','application/javascript');res.end(u.pathname==='/htmx.js'?htmx:policy);return;} + if(u.pathname==='/news'){res.end('

Background news updated

');return;} + if(u.pathname!=='/articles'){res.writeHead(404);res.end('Not found');return;} + const topic=u.searchParams.get('topic')==='beta'?'beta':'alpha',mode=u.searchParams.get('mode')==='managed'?'managed':'raw'; + const target=`/articles?topic=${topic==='alpha'?'beta':'alpha'}&mode=${mode}`; + const boost='hx-boost="true" hx-target="#content" hx-select="#content" hx-swap="outerHTML"'; + const content=`

${topic}

Article ${topic}

Fail enhanced navigationNative recoveryDelayed navigation
`; + const body=hx?content:`${topic} — Catalog${content}

`; + const send=()=>{res.writeHead(hx&&u.searchParams.has('fail')?503:200,{'Content-Type':'text/html'});res.end(hx&&u.searchParams.has('fail')?'Outage':body);}; + if(hx&&u.searchParams.has('slow')){release=send;heldResolve?.();}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={};const output=process.env.FEP_RESULT_PATH||'/tmp/fep-focus-result.json'; +async function inspect(p){return p.evaluate(()=>({focus:document.activeElement.id||document.activeElement.tagName,title:document.title,url:location.href,heading:document.querySelector('h1')?.textContent,feedback:document.querySelector('#feedback')?.textContent,statusRole:document.querySelector('#feedback')?.getAttribute('role'),live:document.querySelector('#feedback')?.getAttribute('aria-live')}));} +async function tabTo(p,id){for(let i=0;i<12;i++){if((await inspect(p)).focus===id)return;await p.keyboard.press('Tab');}throw Error(`Tab failed to reach ${id}`);} +async function selected(p,topic){await p.waitForFunction(t=>document.querySelector('h1')?.textContent===t,topic);assert.equal(new URL(p.url()).searchParams.get('topic'),topic);} +async function settled(p){await p.waitForFunction(()=>window.probe.some(e=>e.name==='htmx:afterSettle'));} +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{ + async function scenario(id,mode,fn){ + requests=[];release=null;const context=await browser.newContext({javaScriptEnabled:mode!=='native'});const p=await context.newPage();p.setDefaultTimeout(4000); + const r={id,engine,mode,status:'pass'}; + try{await p.goto(base+`/articles?topic=alpha&mode=${mode}`);if(mode!=='native')await p.waitForFunction(()=>window.htmx?.version==='2.0.11');r.observation=await fn(p);} + 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.requests=[...requests];results.push(r); + if(release){release();release=null;}heldResolve=null;await context.close();await writeFile(output,JSON.stringify({status:'incomplete',results},null,2)+'\n'); + } + await scenario('K01-native-keyboard','native',async p=>{await tabTo(p,'next');await p.keyboard.press('Enter');await selected(p,'beta');assert.equal(await p.title(),'beta — Catalog');return {keyboardLink:true};}); + for(const mode of ['raw','managed']){ + await scenario('K02-enhanced-keyboard',mode,async p=>{ + await tabTo(p,'next');await p.keyboard.press('Enter');await selected(p,'beta');await settled(p);const o=await inspect(p); + o.headingContinuation=o.focus==='heading';o.updatedTitle=o.title==='beta — Catalog';o.completionFeedback=o.feedback==='Loaded beta'; + if(mode==='managed')assert(o.headingContinuation&&o.updatedTitle&&o.completionFeedback);return o; + }); + await scenario('K03-error-feedback-recovery',mode,async p=>{ + await tabTo(p,'broken');await p.keyboard.press('Enter');await p.waitForFunction(()=>window.probe.some(e=>e.name==='htmx:responseError'));await selected(p,'alpha');const o=await inspect(p);assert.equal(o.focus,'broken'); + o.failureFeedback=o.feedback.includes('Navigation failed');if(mode==='managed'){assert(o.failureFeedback);assert.equal(o.statusRole,'status');assert.equal(o.live,'polite');} + await tabTo(p,'recover');await p.keyboard.press('Enter');await selected(p,'beta');o.nativeRecovery=true;return o; + }); + await scenario('K04-delayed-new-focus',mode,async p=>{ + const held=new Promise(resolve=>{heldResolve=resolve;});await tabTo(p,'slow');await p.keyboard.press('Enter');await held;await tabTo(p,'search'); + release();release=null;await selected(p,'beta');await settled(p);const o=await inspect(p);assert.equal(o.focus,'search');if(mode==='managed')assert.equal(o.feedback,'Loaded beta');return o; + }); + await scenario('K05-history-continuation',mode,async p=>{ + await tabTo(p,'next');await p.keyboard.press('Enter');await selected(p,'beta');await settled(p);await p.goBack();await selected(p,'alpha');await p.waitForFunction(()=>window.probe.some(e=>e.name==='htmx:historyRestore')); + const o=await inspect(p);if(mode==='managed'){assert.equal(o.focus,'heading');assert.equal(o.title,'alpha — Catalog');assert.equal(o.feedback,'Loaded alpha');}return o; + }); + await scenario('K06-background-no-theft',mode,async p=>{ + await tabTo(p,'search');await p.evaluate(()=>htmx.ajax('GET','/news',{target:'#news',swap:'innerHTML'}));await p.locator('#news').filter({hasText:'Background news updated'}).waitFor(); + const o=await inspect(p);assert.equal(o.focus,'search');assert.equal(o.feedback,'');assert.equal(o.title,'alpha — Catalog');return o; + }); + } + }finally{await browser.close();} + } +}finally{if(release)release();await new Promise(resolve=>server.close(resolve));} +const files={};for(const f of ['focus.mjs','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:'keyboard-focus-dom-observation-not-assistive-technology-validation',files,browsers,results,passed:results.filter(x=>x.status==='pass').length,total:results.length};await writeFile(output,JSON.stringify(r,null,2)+'\n');console.log(JSON.stringify({output,passed:r.passed,total:r.total,failures:results.filter(x=>x.status==='fail')},null,2));if(r.passed!==r.total)process.exitCode=1; diff --git a/research/reviews/NavigationCoverage.md b/research/reviews/NavigationCoverage.md index 8980f6a..ebf7106 100644 --- a/research/reviews/NavigationCoverage.md +++ b/research/reviews/NavigationCoverage.md @@ -33,3 +33,10 @@ corroboration to VC-03, URL-addressable State and Progressive Enhancement. Direc native/enhanced/history and representation-cache subset exercised in two browsers; missing-Vary negative control reproduced. VC-03 stays provisional and offline/ accessibility work remains open. No maturity or M1/M2 promotion. + +## Keyboard continuation evidence — 2026-09-27 + +[NF01–NF06](NavigationFocusExperiment.md) adds 22 native/raw/managed observations to +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. diff --git a/research/reviews/NavigationFocusExperiment.md b/research/reviews/NavigationFocusExperiment.md new file mode 100644 index 0000000..20fc902 --- /dev/null +++ b/research/reviews/NavigationFocusExperiment.md @@ -0,0 +1,85 @@ +# Keyboard continuation and navigation feedback — 2026-09-27 + +LEARN-0012; Codex author-model review. [Preregistered protocol](../evolution/cycles/2026-09-27-navigation-focus.md) +committed as `2b1c9a4` before implementation/execution. New stream-F comparison group +F-navigation-focus-v1, not comparable to the preceding HTTP/cache question's group. +[Runner](../experiments/navigation-history/focus.mjs), +[authored policy](../experiments/navigation-history/focus-policy.js), +[receipt](../experiments/navigation-history/focus-result.json). + +## Evidence and result + +Pinned htmx 2.0.11, Playwright 1.63.0; same binary versions/hashes as the previous +navigation cycle: Chromium 151.0.7922.34 and Firefox 155.0. One run, no harness +corrections or discarded failures. 22/22 registered rows completed: two native cases, +ten raw observation cases, ten managed-policy cases. A passing raw observation row +means it was recorded successfully, not that it satisfies the managed obligations. +All browser actions initiating navigation use Tab/Enter; background update is +explicitly script-triggered and browser history uses Playwright's history command. + +| Claim | Cases | Observations in both browsers | +| --- | --- | --- | +| NF01 | K01 | With scripts disabled, Tab reaches the native link and Enter loads beta with its full-document title | +| NF02 | K02 | Raw htmx retains focus on the new same-ID `next` link, but this fragment response leaves alpha's title over beta content and produces no completion status. Managed policy focuses beta's heading, updates title and writes `Loaded beta` | +| NF03 | K03 | Enhanced 503 preserves alpha URL/content and the invoking link's focus in both variants. Only managed policy writes the failure/recovery message; native recovery works by keyboard in both | +| NF04 | K04 | While a response is held at the server, the user tabs to persistent search. Both variants preserve search focus on release; managed policy reports completion without moving focus | +| NF05 | K05 | History restores alpha. Raw focus is the same-ID link; managed focus is the heading with matching title/status | +| NF06 | K06 | An unrelated background region update preserves search focus and does not emit navigation feedback in either variant | + +The raw result narrows our anticipation of focus loss: stable IDs let htmx restore +focus to a replacement element. Here that link now leads back to the other article. +DOM identity retention alone is not proof of an appropriate next task, but neither is +this observation proof that retaining the link is always wrong. The server deliberately +returns a fragment without title metadata; this is an application contract gap, not +a claim that htmx cannot update document titles. + +## Mechanism and interpretation + +The authored policy remembers the request source, checks focus ownership immediately +before a zero-delay swap, and applies the continuation synchronously on afterSwap, +after the library's own focus restoration. It updates heading/title/status only for +registered content navigation. It leaves a surviving search control alone if focus +has moved there. Errors keep focus on the invoker and provide a visible polite status +message with a native recovery action already present. History uses a separate explicit +heading policy. It is a small experiment, not a production navigation library. + +[W3C focus-order guidance](https://www.w3.org/WAI/WCAG22/Understanding/focus-order.html) +allows more than one meaningful focus order and does not prescribe one universal target. +[Status-message guidance](https://www.w3.org/WAI/WCAG22/Understanding/status-messages.html) +informs making feedback programmatically available without requiring focus movement. +[htmx events](https://htmx.org/events/) supplies the lifecycle hooks. Living primary +pages accessed 2026-09-27; exact documentation revisions unknown. Package implementation +is pinned and hashed. DOM status/ARIA observations are not evidence that a screen reader +spoke the message; no assistive technology or human evaluation was run. + +## Disposition and limits + +**Corroboration**, one bounded outcome. Existing Progressive Enhancement already calls +for focus, feedback and recovery semantics; Focus Return explicitly excludes automatic +generalization from modal closure to route transitions. The experiment makes that +boundary concrete and adds an ownership-sensitive candidate mechanism. No newly +invented pattern, canonical promotion, independent recurrence or usefulness gain. + +Add scoped implementation guidance: distinguish successful route continuation, failed +navigation, history and background updates. Test whether a late response still owns +focus before moving it. Returning to a modal invoker and continuing into a new page +are different decisions. Keep both candidates as hypotheses. + +Unverified: screen-reader speech (including possible heading/status duplication), +focus visibility, complete names/order audit, actual usability, pointer modality, +nonzero swap delays, overlapping/superseded route requests, delayed history responses +while the user moves focus, script-late activation, interrupted transport and offline +availability. The explicit history policy always focuses a heading in the tested +immediate restore; it is not proven safe for an asynchronous history miss. Public +single-response success does not settle these ownership races. No writes or production +application were exercised. + +[Effort receipt](../evolution/cycles/2026-09-27-navigation-focus-effort.json) separates +monotonic active agent time from experiment/check subprocess time; final administrative +refresh/commit/snapshot/sync overhead excluded. No token/cost estimate. One cycle in +this new comparison group cannot establish a trend, even though the ledger now has +two prospective cycles in different groups. + +WP-0003 T01 retains delayed history restoration/overlapping navigation ownership and +interrupted enhancement. WP-0003 T02 retains independent human/AT validation and the +offline branch. WP-0006 T03 retains comparable-window and remaining review-date work. diff --git a/research/walkthroughs/VC03-ContentPublication.md b/research/walkthroughs/VC03-ContentPublication.md index bd274a2..5669bb7 100644 --- a/research/walkthroughs/VC03-ContentPublication.md +++ b/research/walkthroughs/VC03-ContentPublication.md @@ -108,3 +108,12 @@ loss under missing Vary. Validator checks use explicit Node requests. This stren the read-only trace; the case remains provisional. Keyboard/focus/announcements, late scripts, interrupted transport, invalid/deleted routes and the offline branch remain open. No performance, independent-human or decision-usefulness gate is completed. + +## Keyboard/feedback subset — 2026-09-27 + +[NF01–NF06](../reviews/NavigationFocusExperiment.md) exercises keyboard links, request +failure/recovery, explicit route continuation and a held response after focus moves +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. diff --git a/workplans/FEP-WP-0003-contrasting-schools.md b/workplans/FEP-WP-0003-contrasting-schools.md index d10f851..473c976 100644 --- a/workplans/FEP-WP-0003-contrasting-schools.md +++ b/workplans/FEP-WP-0003-contrasting-schools.md @@ -201,3 +201,17 @@ WP-0006 T03 remains progress for comparable-window review and remaining substant review-date reconciliation. Choose a new comparison group for materially different questions. M1/M2 and independent-human validation remain open. See research/reviews/NavigationHistoryExperiment.md. + +## Measured keyboard/focus cycle — 2026-09-27 + +LEARN-0012 registered at 2b1c9a4 in new comparison group F-navigation-focus-v1. +22/22 native/raw/managed rows complete; raw rows are observations, not fulfillment +of managed obligations. Same-ID focus retained by raw htmx, while authored policy +adds heading/title/status continuation and preserves newer search focus. Outcome +corroboration, hypotheses retained; no AT or human validation, no trend across the +two different prospective groups. See research/reviews/NavigationFocusExperiment.md. + +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. diff --git a/workplans/FEP-WP-0006-language-evolution.md b/workplans/FEP-WP-0006-language-evolution.md index 038a60c..924bdb7 100644 --- a/workplans/FEP-WP-0006-language-evolution.md +++ b/workplans/FEP-WP-0006-language-evolution.md @@ -80,3 +80,17 @@ WP-0006 T03 remains progress for comparable-window review and remaining substant review-date reconciliation. Choose a new comparison group for materially different questions. M1/M2 and independent-human validation remain open. See research/reviews/NavigationHistoryExperiment.md. + +## Measured keyboard/focus cycle — 2026-09-27 + +LEARN-0012 registered at 2b1c9a4 in new comparison group F-navigation-focus-v1. +22/22 native/raw/managed rows complete; raw rows are observations, not fulfillment +of managed obligations. Same-ID focus retained by raw htmx, while authored policy +adds heading/title/status continuation and preserves newer search focus. Outcome +corroboration, hypotheses retained; no AT or human validation, no trend across the +two different prospective groups. See research/reviews/NavigationFocusExperiment.md. + +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.