Test competing navigation ownership and review research thread
Assistant: codex Assistant-Model: gpt-6-astra Assistant-Session: 01a0cffe-206f-7323-89d3-b20ca4cf4bf9
This commit is contained in:
parent
e484cae45f
commit
e8ccb73a12
22 changed files with 4981 additions and 13 deletions
9
TREE.md
9
TREE.md
|
|
@ -353,6 +353,8 @@ research/evolution/context.json
|
|||
research/evolution/cycles/2026-09-27-navigation-effort.json
|
||||
research/evolution/cycles/2026-09-27-navigation-focus-effort.json
|
||||
research/evolution/cycles/2026-09-27-navigation-focus.md
|
||||
research/evolution/cycles/2026-09-27-navigation-ownership-effort.json
|
||||
research/evolution/cycles/2026-09-27-navigation-ownership.md
|
||||
research/evolution/cycles/2026-09-27-navigation.md
|
||||
research/evolution/ledger.json
|
||||
research/evolution/profiles.json
|
||||
|
|
@ -407,6 +409,11 @@ research/experiments/navigation-history/focus-policy.js
|
|||
research/experiments/navigation-history/focus-result.json
|
||||
research/experiments/navigation-history/focus.mjs
|
||||
research/experiments/navigation-history/initial-partial.json
|
||||
research/experiments/navigation-history/ownership-initial-result.json
|
||||
research/experiments/navigation-history/ownership-initial.mjs
|
||||
research/experiments/navigation-history/ownership-policy.js
|
||||
research/experiments/navigation-history/ownership-result.json
|
||||
research/experiments/navigation-history/ownership.mjs
|
||||
research/experiments/navigation-history/package-lock.json
|
||||
research/experiments/navigation-history/package.json
|
||||
research/experiments/navigation-history/result.json
|
||||
|
|
@ -431,7 +438,9 @@ research/reviews/MatrixRecoveryExperiment.md
|
|||
research/reviews/NavigationCoverage.md
|
||||
research/reviews/NavigationFocusExperiment.md
|
||||
research/reviews/NavigationHistoryExperiment.md
|
||||
research/reviews/NavigationOwnershipExperiment.md
|
||||
research/reviews/NavigationRenderingEvidence.md
|
||||
research/reviews/NavigationThreadReview.md
|
||||
research/reviews/NestedDismissalIsolation.md
|
||||
research/reviews/NestedOwnershipRouting.md
|
||||
research/reviews/ObsidianConflictScreen.json
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
}
|
||||
|
|
@ -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": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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": []
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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
|
|
@ -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;
|
||||
31
research/experiments/navigation-history/ownership-policy.js
Normal file
31
research/experiments/navigation-history/ownership-policy.js
Normal 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}));
|
||||
2301
research/experiments/navigation-history/ownership-result.json
Normal file
2301
research/experiments/navigation-history/ownership-result.json
Normal file
File diff suppressed because it is too large
Load diff
64
research/experiments/navigation-history/ownership.mjs
Normal file
64
research/experiments/navigation-history/ownership.mjs
Normal 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;
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
95
research/reviews/NavigationOwnershipExperiment.md
Normal file
95
research/reviews/NavigationOwnershipExperiment.md
Normal 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.
|
||||
59
research/reviews/NavigationThreadReview.md
Normal file
59
research/reviews/NavigationThreadReview.md
Normal 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.
|
||||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue