Compare commits

...

3 commits

Author SHA1 Message Date
9c8245f507 Snapshot navigation completion ownership cycle
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a0cffe-206f-7323-89d3-b20ca4cf4bf9
2026-09-27 00:45:56 +02:00
e8ccb73a12 Test competing navigation ownership and review research thread
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a0cffe-206f-7323-89d3-b20ca4cf4bf9
2026-09-27 00:45:54 +02:00
e484cae45f Preregister competing navigation ownership comparison
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a0cffe-206f-7323-89d3-b20ca4cf4bf9
2026-09-27 00:30:14 +02:00
24 changed files with 6260 additions and 13 deletions

10
TREE.md
View file

@ -353,11 +353,14 @@ 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
research/evolution/snapshots/baseline-2026-09-26.json
research/evolution/snapshots/cycle-2026-09-27-navigation-focus.json
research/evolution/snapshots/cycle-2026-09-27-navigation-ownership.json
research/evolution/snapshots/cycle-2026-09-27-navigation.json
research/examples/SchemaEvidence.md
research/examples/schema-contract.yaml
@ -407,6 +410,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 +439,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

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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."
}

View file

@ -0,0 +1,64 @@
# LEARN-0013 — Completion ownership under competing navigation
Status: preregistered before implementation/execution. 2026-09-27 Europe/Berlin.
Codex author-model review; stream F, new comparison group F-navigation-ownership-v1.
The multi-request question differs from the single-response focus protocol; do not
pool rates. CAND-0009 URL-addressable State, CAND-0010 Progressive Enhancement,
CAND-0016 Focus Return. No new candidate presumed.
## Question and prior
Can the previous managed focus policy let old history/navigation completions replace
newer selection, steal focus or report obsolete results? Prior single-request evidence
supports continuation but leaves competing completions untested. Proposed guard:
assign an owner/generation to navigation, reject stale completions before effects,
and track focus ownership independently so an otherwise valid response need not
move focus. Test outcome, not a blanket recommendation to cancel HTTP requests.
Compare the unchanged prior focus-policy.js with an authored guarded policy on one
new controlled fixture. Use stable content target and persistent navigation controls
so requests from different elements can compete. Hold responses at server barriers,
then release in declared order. Browser history uses real Back with htmx snapshot
eviction. Keep default transport alive to distinguish ignored completion from abort.
If htmx prevents a race itself, record that rather than force a failure. A guarded
miss/error or URL/content mismatch is failure, not permission to revise expectations.
## Registered matrix
Two policies × two browsers (Chromium/Firefox), six cases = 24 rows. Baseline rows
record the same obligation booleans as guarded rows; distinguish successful recording
from meeting the obligations. Guarded rows assert all registered obligations. One
trial each; preserve failures and any correction. No arbitrary sleeps. Tab/Enter
activate links; browser Back uses history API through Playwright.
| ID | Ordered scenario | Obligation after completion |
| --- | --- | --- |
| O01 | Back misses history cache; user moves to surviving search; release history | Restore selected article/URL/title but preserve search focus and typed text |
| O02 | Back miss held; newer article navigation completes; release old history | Keep newer article/URL/title/status/focus; old history completion has no UI effect |
| O03 | Older article request held; newer article completes; release older success | Keep newer article/URL/title/status; no late focus theft |
| O04 | Older request held and will fail; newer article completes; release old 503 | Preserve newer result and feedback; no obsolete error replaces completion |
| O05 | Older request held; newer request fails; then release older success | Do not resurrect older intent; retain last committed view with newest failure feedback |
| O06 | Older article held; Back restores cached view; release old success | History is newer intent too: keep restored view/URL/title/status/focus |
O05 starts from an already committed alpha view; O06 first visits beta to establish
history. Distinguish committed route from pending browser-history URL. Capture
intermediate and final state, actual requests and completion traces. Prefer public
hooks; any history-XHR callback wrapping is explicitly pinned/experimental, not a
portable production fix. No writes, server rollback, AT speech, production recurrence,
nonzero swap delays, view transitions, redirect/error taxonomy or universal race proof.
## Learning and stopping
45 active research minutes and 10 execution minutes before scope review. Monotonic
single-agent elapsed session minus experiment/check subprocess durations; checkpoint
inspection belongs to execution, no double count. Orientation and final administration
outside window; tokens/cost unknown. Commit this plan before implementation/execution.
Append one ledger outcome; retain all results and source hashes. If scope/criteria
must change, preserve and disclose the original plan rather than rewriting it.
After this comparison, pause the navigation-fixture thread. Write a short synthesis:
what changed versus prior claims, what remains unverified, and the next independent
or broader evidence question. Hand residuals to WP-0003 T01/T02 and measurement work
to WP-0006 T03. Do not automatically start another fixture hardening cycle. One cycle
per distinct group cannot demonstrate diminishing returns. Live inference, if needed,
uses llm-connect; none is planned here.

View file

@ -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": []
}
]
}

View file

@ -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": []
},

File diff suppressed because it is too large Load diff

View file

@ -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.

File diff suppressed because it is too large Load diff

View file

@ -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=`<div id="payload"><h1 id="heading" tabindex="-1">${topic}</h1><p>Article ${topic}</p></div>`;
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=`<nav><a id="beta" href="${href('beta')}" ${boost}>Visit beta</a><a id="old" href="${href('beta','&hold=old')}" ${boost}>Slow beta</a><a id="oldfail" href="${href('beta','&hold=old&fail=1')}" ${boost}>Slow failed beta</a><a id="new" href="${href('gamma')}" ${boost}>Visit gamma</a><a id="newfail" href="${href('gamma','&fail=1')}" ${boost}>Failed gamma</a><a id="recover" href="${href('gamma')}">Native recovery</a></nav>`;
const full=`<!doctype html><html lang="en"><head><meta charset="utf-8"><title>${topic} — Catalog</title><meta name="htmx-config" content='{"historyRestoreAsHxRequest":false}'><script src="/trace.js" defer></script><script src="/${mode==='guarded'?'guarded':'baseline'}.js" defer></script><script src="/htmx.js" defer></script></head><body>${nav}<main id="content" hx-history-elt>${content}</main><label>Persistent search <input id="search"></label><p id="feedback" role="status" aria-live="polite"></p></body></html>`;
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;

View file

@ -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}));

File diff suppressed because it is too large Load diff

View file

@ -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=`<div id="payload"><h1 id="heading" tabindex="-1">${topic}</h1><p>Article ${topic}</p></div>`;
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=`<nav><a id="beta" href="${href('beta')}" ${boost}>Visit beta</a><a id="old" href="${href('beta','&hold=old')}" ${boost}>Slow beta</a><a id="oldfail" href="${href('beta','&hold=old&fail=1')}" ${boost}>Slow failed beta</a><a id="new" href="${href('gamma')}" ${boost}>Visit gamma</a><a id="newfail" href="${href('gamma','&fail=1')}" ${boost}>Failed gamma</a><a id="recover" href="${href('gamma')}">Native recovery</a></nav>`;
const full=`<!doctype html><html lang="en"><head><meta charset="utf-8"><title>${topic} — Catalog</title><meta name="htmx-config" content='{"historyRestoreAsHxRequest":false}'><script src="/trace.js" defer></script><script src="/${mode==='guarded'?'guarded':'baseline'}.js" defer></script><script src="/htmx.js" defer></script></head><body>${nav}<main id="content" hx-history-elt>${content}</main><label>Persistent search <input id="search"></label><p id="feedback" role="status" aria-live="polite"></p></body></html>`;
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;

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.

View file

@ -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.