Measure navigation history and cache corroboration cycle

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a0cffe-206f-7323-89d3-b20ca4cf4bf9
This commit is contained in:
tegwick 2026-09-27 00:11:21 +02:00
parent f3697c1447
commit d32774da51
20 changed files with 2917 additions and 24 deletions

View file

@ -0,0 +1,40 @@
# Navigation, history and representation-cache experiment
First prospective cycle: [preregistration](../../evolution/cycles/2026-09-27-navigation.md)
at commit `f3697c1`, before implementation/execution. [Review](../../reviews/NavigationHistoryExperiment.md).
Pinned htmx 2.0.11 and Playwright 1.63.0, authored public read-only HTTP fixture.
[Result](result.json) includes input hashes, exact browser versions/binary hashes,
request traces and final DOM/event observations. No real application/CDN or model call.
```bash
npm ci --ignore-scripts --cache /tmp/fep-npm-cache
PLAYWRIGHT_BROWSERS_PATH=/tmp/fep-playwright-browsers node node_modules/playwright/cli.js install firefox
PLAYWRIGHT_BROWSERS_PATH=/tmp/fep-playwright-browsers FEP_CHROMIUM_PATH=/path/to/chrome npm test
```
Firefox profile access required execution outside the workspace sandbox. Chromium
was an explicit preinstalled binary override, not claimed to be Playwright's bundled
revision. Default output `/tmp/fep-navigation-result.json`; `FEP_RESULT_PATH` overrides
it. The runner writes checkpoints to that output and a complete receipt on completion.
Choose a new output per run; committed receipts are never default outputs.
[Initial partial receipt](initial-partial.json) retains ten passing Chromium rows;
launch then failed because Firefox was not in Playwright's default cache. Setting the
existing browser-cache path fixed execution, without changing runner or criteria.
The full run repeated Chromium and completed Firefox: 20/20 registered checks, not
30 independent observations. N10 repeats explicit Node HTTP checks in each browser
group; those two rows are not evidence of browser-specific automatic revalidation.
N09 records the negative control, including non-reproduction if that ever occurs.
N01–N06 cover direct/native/enhanced/history/recovery paths; N07–N09 use actual browser
HTTP cache with no request interception. N08's final full fetch reaches the server
again in both browsers: correctness does not imply simultaneous retention of both
variants or optimal cache hit rate. Both N09 runs reuse the fragment without another
request. The server varies on both request selectors and computes ETags from response
bytes. htmx history requests explicitly disable HX-Request; the server separately
supports the history-restore header overriding fragment selection.
History eviction uses htmx 2.0.11's sessionStorage key, an experimental control tied
to the pinned implementation. No assertion of identical content freshness across
history, general offline availability, accessibility, script-late races, writes,
real intermediary correctness or independent full-pattern recurrence follows.

View file

@ -0,0 +1,114 @@
import {chromium, firefox} from 'playwright';
import {createServer} from 'node:http';
import {once} from 'node:events';
import {readFile, writeFile} from 'node:fs/promises';
import {createHash} from 'node:crypto';
import assert from 'node:assert/strict';
const dir=new URL('.',import.meta.url);
const htmx=await readFile(new URL('node_modules/htmx.org/dist/htmx.js',dir));
let requests=[];
const observer=`window.probe=[];for(const name of ['htmx:afterSettle','htmx:historyRestore','htmx:responseError'])document.addEventListener(name,e=>window.probe.push({name,url:location.href,cacheMiss:e.detail.cacheMiss}));`;
const server=createServer((req,res)=>{
const url=new URL(req.url,'http://localhost');
const hx=req.headers['hx-request']==='true', restore=req.headers['hx-history-restore-request']==='true';
let status=200,body='',representation='asset';
res.setHeader('Cache-Control','no-store');
if(url.pathname==='/htmx.js'){
status=url.searchParams.has('fail')?503:200;body=status===200?htmx:'blocked enhancement';res.setHeader('Content-Type','application/javascript');
}else if(url.pathname==='/observe.js'){body=observer;res.setHeader('Content-Type','application/javascript');}
else if(['/articles','/cache-good','/cache-bad'].includes(url.pathname)){
const topic=url.searchParams.get('topic')==='beta'?'beta':'alpha';
const fail=url.searchParams.has('scriptfail')?'&scriptfail=1':'';
const main=`<main id="content" hx-history-elt><h1>${topic}</h1><p id="article">Public article ${topic}</p><a id="next" href="${url.pathname}?topic=${topic==='alpha'?'beta':'alpha'}${fail}" hx-boost="true" hx-target="#content" hx-select="#content" hx-swap="outerHTML">Other topic</a><form action="${url.pathname}" method="get"><label>Topic <select name="topic"><option>alpha</option><option>beta</option></select></label><button>Filter</button></form><a id="broken" href="/articles?topic=beta&enhancedfail=1" hx-boost="true" hx-target="#content" hx-select="#content" hx-swap="outerHTML">Injected enhanced failure</a><a id="recover" href="/articles?topic=beta&enhancedfail=1">Native recovery</a></main>`;
representation=hx&&!restore?'fragment':'full';
const full=`<!doctype html><html lang="en"><head><meta charset="utf-8"><title>Public catalog</title><meta name="htmx-config" content='{"historyRestoreAsHxRequest":false}'><script src="/observe.js"></script><script src="/htmx.js${fail?'?fail=1':''}" defer></script></head><body><header id="shell">Catalog shell</header>${main}</body></html>`;
body=representation==='full'?full:main;
if(hx&&url.searchParams.has('enhancedfail')){status=503;body='Injected enhancement outage';}
res.setHeader('Content-Type','text/html; charset=utf-8');
if(url.pathname.startsWith('/cache-')){
res.setHeader('Cache-Control','public, max-age=3600');
const etag='"'+createHash('sha256').update(body).digest('hex')+'"';res.setHeader('ETag',etag);
if(url.pathname==='/cache-good')res.setHeader('Vary','HX-Request, HX-History-Restore-Request');
if(req.headers['if-none-match']===etag){status=304;body='';}
}
}
else{status=404;body='Not found';}
requests.push({path:req.url,hx,restore,ifNoneMatch:req.headers['if-none-match']||null,status,representation,vary:res.getHeader('Vary')||null,etag:res.getHeader('ETag')||null});
res.writeHead(status);res.end(body);
});
server.listen(0,'127.0.0.1');await once(server,'listening');
const base=`http://127.0.0.1:${server.address().port}`;
const results=[], browsers={};
const output=process.env.FEP_RESULT_PATH||'/tmp/fep-navigation-result.json';
try{
for(const [engine,launcher] of [['chromium',chromium],['firefox',firefox]]){
const executablePath=engine==='chromium'?process.env.FEP_CHROMIUM_PATH:undefined;
const browser=await launcher.launch({headless:true,...(executablePath?{executablePath}:{})});
const binary=executablePath||launcher.executablePath();
browsers[engine]={version:browser.version(),executable:binary,sha256:createHash('sha256').update(await readFile(binary)).digest('hex')};
try{
async function scenario(id,run,javaScriptEnabled=true){
requests=[];const context=await browser.newContext({javaScriptEnabled});const page=await context.newPage();page.setDefaultTimeout(4000);
const result={id,engine,status:'pass'};
try{result.observation=await run(page,context);}catch(e){result.status='fail';result.error=e.message;}
result.requests=[...requests];result.url=page.url();result.body=await page.locator('body').innerText().catch(()=>null);
result.events=javaScriptEnabled?await page.evaluate(()=>window.probe||[]).catch(()=>[]):[];
results.push(result);await context.close();
await writeFile(output,JSON.stringify({status:'incomplete',results},null,2)+'\n');
}
async function selected(p,topic){await p.locator('h1').filter({hasText:topic}).waitFor();assert.equal(new URL(p.url()).searchParams.get('topic'),topic);}
async function enhanced(p){await p.waitForFunction(()=>window.htmx?.version==='2.0.11');}
async function next(p,topic){await p.locator('#next').click();await selected(p,topic);await p.waitForFunction(()=>window.probe.some(e=>e.name==='htmx:afterSettle'));}
await scenario('N01-no-script-native',async p=>{
await p.goto(base+'/articles?topic=alpha');await selected(p,'alpha');assert.equal(await p.locator('#shell').count(),1);
await p.locator('#next').click();await selected(p,'beta');await p.locator('select').selectOption('alpha');await p.getByRole('button',{name:'Filter'}).click();await selected(p,'alpha');
assert(requests.filter(r=>r.path.startsWith('/articles')).every(r=>!r.hx&&r.representation==='full'));return {directLinkAndGetForm:true,fullShell:true};
},false);
await scenario('N02-enhance-reload-direct',async(p,c)=>{
await p.goto(base+'/articles?topic=alpha');await enhanced(p);await next(p,'beta');assert(requests.some(r=>r.hx&&r.representation==='fragment'));
await p.reload();await selected(p,'beta');assert.equal(await p.locator('#shell').count(),1);
const fresh=await c.newPage();await fresh.goto(base+'/articles?topic=beta');assert.equal(await fresh.locator('#shell').count(),1);await fresh.close();return {pushReloadAndFreshEntry:true};
});
await scenario('N03-history-hit',async p=>{
await p.goto(base+'/articles?topic=alpha');await enhanced(p);await next(p,'beta');const before=requests.length;
await p.goBack();await selected(p,'alpha');await p.waitForFunction(()=>window.probe.some(e=>e.name==='htmx:historyRestore'));
await p.goForward();await selected(p,'beta');
assert(!requests.slice(before).some(r=>r.path.startsWith('/articles')));return {backForward:true,additionalArticleRequests:0};
});
await scenario('N04-history-miss',async p=>{
await p.goto(base+'/articles?topic=alpha');await enhanced(p);await next(p,'beta');await p.evaluate(()=>sessionStorage.removeItem('htmx-history-cache'));
await p.goBack();await selected(p,'alpha');assert(requests.some(r=>r.restore&&r.representation==='full'&&!r.hx));return {evictedSnapshotFetchedFull:true};
});
await scenario('N05-enhancement-failure',async p=>{
await p.goto(base+'/articles?topic=alpha');await enhanced(p);await p.locator('#broken').click();await p.waitForFunction(()=>window.probe.some(e=>e.name==='htmx:responseError'));await selected(p,'alpha');
assert(requests.some(r=>r.hx&&r.status===503));await p.locator('#recover').click();await selected(p,'beta');assert.equal(await p.locator('#shell').count(),1);return {oldViewAndUrlRetained:true,explicitNativeRecovery:true};
});
await scenario('N06-script-failure',async p=>{
await p.goto(base+'/articles?topic=alpha&scriptfail=1');assert(requests.some(r=>r.path==='/htmx.js?fail=1'&&r.status===503));assert.equal(await p.evaluate(()=>typeof window.htmx),'undefined');
await p.locator('#next').click();await selected(p,'beta');await p.locator('select').selectOption('alpha');await p.getByRole('button',{name:'Filter'}).click();await selected(p,'alpha');return {nativeLinkAndFormDespiteScript503:true};
});
async function cachedFetch(p,path,hx=false){return p.evaluate(async({path,hx})=>{const r=await fetch(path,{headers:hx?{'HX-Request':'true'}:{}});return {status:r.status,text:await r.text(),etag:r.headers.get('etag')};},{path,hx});}
await scenario('N07-fragment-before-document',async p=>{
await p.goto(base+'/articles?topic=alpha');const path='/cache-good?topic=beta';const frag=await cachedFetch(p,path,true);assert(!frag.text.includes('id="shell"'));
await p.goto(base+path);assert.equal(await p.locator('#shell').count(),1);await selected(p,'beta');return {fragmentThenFull:true};
});
await scenario('N08-document-before-fragment',async p=>{
const path='/cache-good?topic=alpha';await p.goto(base+path);const frag=await cachedFetch(p,path,true);assert(!frag.text.includes('id="shell"'));assert(frag.text.includes('<main'));
const full=await cachedFetch(p,path);assert(full.text.includes('id="shell"'));return {fullThenFragmentThenFull:true};
});
await scenario('N09-missing-vary-negative',async p=>{
await p.goto(base+'/articles?topic=alpha');const path='/cache-bad?topic=beta';await cachedFetch(p,path,true);const before=requests.length;await p.goto(base+path);
return {fragmentServedAsDocument:await p.locator('#shell').count()===0,additionalRequests:requests.slice(before),negativeControl:true};
});
await scenario('N10-conditional-representations',async()=>{
const path=base+'/cache-good?topic=beta';const fragment=await fetch(path,{headers:{'HX-Request':'true'}});const etag=fragment.headers.get('etag');await fragment.text();
const same=await fetch(path,{headers:{'HX-Request':'true','If-None-Match':etag}});assert.equal(same.status,304);
const full=await fetch(path,{headers:{'If-None-Match':etag}});assert.equal(full.status,200);assert((await full.text()).includes('id="shell"'));assert.notEqual(full.headers.get('etag'),etag);
const restore=await fetch(path,{headers:{'HX-Request':'true','HX-History-Restore-Request':'true'}});assert((await restore.text()).includes('id="shell"'));return {transport:'explicit Node HTTP requests; duplicated per browser group, not independent browser evidence',sameRepresentation:304,crossRepresentation:200,restoreFull:true};
});
}finally{await browser.close();}
}
}finally{await new Promise(resolve=>server.close(resolve));}
const files={};for(const f of ['experiment.mjs','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 receipt={date:new Date().toISOString(),node:process.version,kind:'authored-http-fixture-with-pinned-htmx',browsers,files,results,passed:results.filter(r=>r.status==='pass').length,total:results.length};
await writeFile(output,JSON.stringify(receipt,null,2)+'\n');console.log(JSON.stringify({output,passed:receipt.passed,total:receipt.total,failures:results.filter(r=>r.status==='fail')},null,2));if(receipt.passed!==receipt.total)process.exitCode=1;

View file

@ -0,0 +1,768 @@
{
"status": "incomplete",
"results": [
{
"id": "N01-no-script-native",
"engine": "chromium",
"status": "pass",
"observation": {
"directLinkAndGetForm": true,
"fullShell": true
},
"requests": [
{
"path": "/articles?topic=alpha",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "full",
"vary": null,
"etag": null
},
{
"path": "/favicon.ico",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 404,
"representation": "asset",
"vary": null,
"etag": null
},
{
"path": "/articles?topic=beta",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "full",
"vary": null,
"etag": null
},
{
"path": "/articles?topic=alpha",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "full",
"vary": null,
"etag": null
}
],
"url": "http://127.0.0.1:39749/articles?topic=alpha",
"body": "Catalog shell\nalpha\n\nPublic article alpha\n\nOther topic\nTopic \nalpha\nbeta\nFilter\nInjected enhanced failureNative recovery",
"events": []
},
{
"id": "N02-enhance-reload-direct",
"engine": "chromium",
"status": "pass",
"observation": {
"pushReloadAndFreshEntry": true
},
"requests": [
{
"path": "/articles?topic=alpha",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "full",
"vary": null,
"etag": null
},
{
"path": "/observe.js",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "asset",
"vary": null,
"etag": null
},
{
"path": "/htmx.js",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "asset",
"vary": null,
"etag": null
},
{
"path": "/articles?topic=beta",
"hx": true,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "fragment",
"vary": null,
"etag": null
},
{
"path": "/articles?topic=beta",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "full",
"vary": null,
"etag": null
},
{
"path": "/observe.js",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "asset",
"vary": null,
"etag": null
},
{
"path": "/htmx.js",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "asset",
"vary": null,
"etag": null
},
{
"path": "/articles?topic=beta",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "full",
"vary": null,
"etag": null
},
{
"path": "/observe.js",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "asset",
"vary": null,
"etag": null
},
{
"path": "/htmx.js",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "asset",
"vary": null,
"etag": null
}
],
"url": "http://127.0.0.1:39749/articles?topic=beta",
"body": "Catalog shell\nbeta\n\nPublic article beta\n\nOther topic\nTopic \nalpha\nbeta\nFilter\nInjected enhanced failureNative recovery",
"events": []
},
{
"id": "N03-history-hit",
"engine": "chromium",
"status": "pass",
"observation": {
"backForward": true,
"additionalArticleRequests": 0
},
"requests": [
{
"path": "/articles?topic=alpha",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "full",
"vary": null,
"etag": null
},
{
"path": "/observe.js",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "asset",
"vary": null,
"etag": null
},
{
"path": "/htmx.js",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "asset",
"vary": null,
"etag": null
},
{
"path": "/articles?topic=beta",
"hx": true,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "fragment",
"vary": null,
"etag": null
}
],
"url": "http://127.0.0.1:39749/articles?topic=beta",
"body": "Catalog shell\nbeta\n\nPublic article beta\n\nOther topic\nTopic \nalpha\nbeta\nFilter\nInjected enhanced failureNative recovery",
"events": [
{
"name": "htmx:afterSettle",
"url": "http://127.0.0.1:39749/articles?topic=beta"
},
{
"name": "htmx:afterSettle",
"url": "http://127.0.0.1:39749/articles?topic=alpha"
},
{
"name": "htmx:historyRestore",
"url": "http://127.0.0.1:39749/articles?topic=alpha"
},
{
"name": "htmx:afterSettle",
"url": "http://127.0.0.1:39749/articles?topic=beta"
},
{
"name": "htmx:historyRestore",
"url": "http://127.0.0.1:39749/articles?topic=beta"
}
]
},
{
"id": "N04-history-miss",
"engine": "chromium",
"status": "pass",
"observation": {
"evictedSnapshotFetchedFull": true
},
"requests": [
{
"path": "/articles?topic=alpha",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "full",
"vary": null,
"etag": null
},
{
"path": "/observe.js",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "asset",
"vary": null,
"etag": null
},
{
"path": "/htmx.js",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "asset",
"vary": null,
"etag": null
},
{
"path": "/articles?topic=beta",
"hx": true,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "fragment",
"vary": null,
"etag": null
},
{
"path": "/articles?topic=alpha",
"hx": false,
"restore": true,
"ifNoneMatch": null,
"status": 200,
"representation": "full",
"vary": null,
"etag": null
}
],
"url": "http://127.0.0.1:39749/articles?topic=alpha",
"body": "Catalog shell\nalpha\n\nPublic article alpha\n\nOther topic\nTopic \nalpha\nbeta\nFilter\nInjected enhanced failureNative recovery",
"events": [
{
"name": "htmx:afterSettle",
"url": "http://127.0.0.1:39749/articles?topic=beta"
},
{
"name": "htmx:afterSettle",
"url": "http://127.0.0.1:39749/articles?topic=alpha"
},
{
"name": "htmx:historyRestore",
"url": "http://127.0.0.1:39749/articles?topic=alpha",
"cacheMiss": true
}
]
},
{
"id": "N05-enhancement-failure",
"engine": "chromium",
"status": "pass",
"observation": {
"oldViewAndUrlRetained": true,
"explicitNativeRecovery": true
},
"requests": [
{
"path": "/articles?topic=alpha",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "full",
"vary": null,
"etag": null
},
{
"path": "/observe.js",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "asset",
"vary": null,
"etag": null
},
{
"path": "/htmx.js",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "asset",
"vary": null,
"etag": null
},
{
"path": "/articles?topic=beta&enhancedfail=1",
"hx": true,
"restore": false,
"ifNoneMatch": null,
"status": 503,
"representation": "fragment",
"vary": null,
"etag": null
},
{
"path": "/articles?topic=beta&enhancedfail=1",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "full",
"vary": null,
"etag": null
},
{
"path": "/observe.js",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "asset",
"vary": null,
"etag": null
},
{
"path": "/htmx.js",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "asset",
"vary": null,
"etag": null
}
],
"url": "http://127.0.0.1:39749/articles?topic=beta&enhancedfail=1",
"body": "Catalog shell\nbeta\n\nPublic article beta\n\nOther topic\nTopic \nalpha\nbeta\nFilter\nInjected enhanced failureNative recovery",
"events": []
},
{
"id": "N06-script-failure",
"engine": "chromium",
"status": "pass",
"observation": {
"nativeLinkAndFormDespiteScript503": true
},
"requests": [
{
"path": "/articles?topic=alpha&scriptfail=1",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "full",
"vary": null,
"etag": null
},
{
"path": "/observe.js",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "asset",
"vary": null,
"etag": null
},
{
"path": "/htmx.js?fail=1",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 503,
"representation": "asset",
"vary": null,
"etag": null
},
{
"path": "/articles?topic=beta&scriptfail=1",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "full",
"vary": null,
"etag": null
},
{
"path": "/observe.js",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "asset",
"vary": null,
"etag": null
},
{
"path": "/htmx.js?fail=1",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 503,
"representation": "asset",
"vary": null,
"etag": null
},
{
"path": "/articles?topic=alpha",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "full",
"vary": null,
"etag": null
},
{
"path": "/observe.js",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "asset",
"vary": null,
"etag": null
},
{
"path": "/htmx.js",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "asset",
"vary": null,
"etag": null
}
],
"url": "http://127.0.0.1:39749/articles?topic=alpha",
"body": "Catalog shell\nalpha\n\nPublic article alpha\n\nOther topic\nTopic \nalpha\nbeta\nFilter\nInjected enhanced failureNative recovery",
"events": []
},
{
"id": "N07-fragment-before-document",
"engine": "chromium",
"status": "pass",
"observation": {
"fragmentThenFull": true
},
"requests": [
{
"path": "/articles?topic=alpha",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "full",
"vary": null,
"etag": null
},
{
"path": "/observe.js",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "asset",
"vary": null,
"etag": null
},
{
"path": "/htmx.js",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "asset",
"vary": null,
"etag": null
},
{
"path": "/cache-good?topic=beta",
"hx": true,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "fragment",
"vary": "HX-Request, HX-History-Restore-Request",
"etag": "\"d4a2e4af8c3671d399ddcbbc92984556a75f795817fba6404ea84e1805892663\""
},
{
"path": "/cache-good?topic=beta",
"hx": false,
"restore": false,
"ifNoneMatch": "\"d4a2e4af8c3671d399ddcbbc92984556a75f795817fba6404ea84e1805892663\"",
"status": 200,
"representation": "full",
"vary": "HX-Request, HX-History-Restore-Request",
"etag": "\"843d827eaa297ed04d5424a03a78a7049356060ac71f73bc7e1b85d3b5691c41\""
},
{
"path": "/observe.js",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "asset",
"vary": null,
"etag": null
},
{
"path": "/htmx.js",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "asset",
"vary": null,
"etag": null
}
],
"url": "http://127.0.0.1:39749/cache-good?topic=beta",
"body": "Catalog shell\nbeta\n\nPublic article beta\n\nOther topic\nTopic \nalpha\nbeta\nFilter\nInjected enhanced failureNative recovery",
"events": []
},
{
"id": "N08-document-before-fragment",
"engine": "chromium",
"status": "pass",
"observation": {
"fullThenFragmentThenFull": true
},
"requests": [
{
"path": "/cache-good?topic=alpha",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "full",
"vary": "HX-Request, HX-History-Restore-Request",
"etag": "\"3a9bd4c1596afe87253606468ccd0909f3748af4a479926917695e13f45b15c3\""
},
{
"path": "/observe.js",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "asset",
"vary": null,
"etag": null
},
{
"path": "/htmx.js",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "asset",
"vary": null,
"etag": null
},
{
"path": "/cache-good?topic=alpha",
"hx": true,
"restore": false,
"ifNoneMatch": "\"3a9bd4c1596afe87253606468ccd0909f3748af4a479926917695e13f45b15c3\"",
"status": 200,
"representation": "fragment",
"vary": "HX-Request, HX-History-Restore-Request",
"etag": "\"af6404a0e852c5f75ab2abf25f61de82db30f85e21abeb19055a6b5caf854229\""
},
{
"path": "/cache-good?topic=alpha",
"hx": false,
"restore": false,
"ifNoneMatch": "\"af6404a0e852c5f75ab2abf25f61de82db30f85e21abeb19055a6b5caf854229\"",
"status": 200,
"representation": "full",
"vary": "HX-Request, HX-History-Restore-Request",
"etag": "\"3a9bd4c1596afe87253606468ccd0909f3748af4a479926917695e13f45b15c3\""
}
],
"url": "http://127.0.0.1:39749/cache-good?topic=alpha",
"body": "Catalog shell\nalpha\n\nPublic article alpha\n\nOther topic\nTopic \nalpha\nbeta\nFilter\nInjected enhanced failureNative recovery",
"events": []
},
{
"id": "N09-missing-vary-negative",
"engine": "chromium",
"status": "pass",
"observation": {
"fragmentServedAsDocument": true,
"additionalRequests": [],
"negativeControl": true
},
"requests": [
{
"path": "/articles?topic=alpha",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "full",
"vary": null,
"etag": null
},
{
"path": "/observe.js",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "asset",
"vary": null,
"etag": null
},
{
"path": "/htmx.js",
"hx": false,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "asset",
"vary": null,
"etag": null
},
{
"path": "/cache-bad?topic=beta",
"hx": true,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "fragment",
"vary": null,
"etag": "\"69c19e9840aa688ca34f4768e33ab28e0b994aeb5191251db7c0ab9a36b2699b\""
}
],
"url": "http://127.0.0.1:39749/cache-bad?topic=beta",
"body": "beta\n\nPublic article beta\n\nOther topic\nTopic \nalpha\nbeta\nFilter\nInjected enhanced failureNative recovery",
"events": []
},
{
"id": "N10-conditional-representations",
"engine": "chromium",
"status": "pass",
"observation": {
"transport": "explicit Node HTTP requests; duplicated per browser group, not independent browser evidence",
"sameRepresentation": 304,
"crossRepresentation": 200,
"restoreFull": true
},
"requests": [
{
"path": "/cache-good?topic=beta",
"hx": true,
"restore": false,
"ifNoneMatch": null,
"status": 200,
"representation": "fragment",
"vary": "HX-Request, HX-History-Restore-Request",
"etag": "\"d4a2e4af8c3671d399ddcbbc92984556a75f795817fba6404ea84e1805892663\""
},
{
"path": "/cache-good?topic=beta",
"hx": true,
"restore": false,
"ifNoneMatch": "\"d4a2e4af8c3671d399ddcbbc92984556a75f795817fba6404ea84e1805892663\"",
"status": 304,
"representation": "fragment",
"vary": "HX-Request, HX-History-Restore-Request",
"etag": "\"d4a2e4af8c3671d399ddcbbc92984556a75f795817fba6404ea84e1805892663\""
},
{
"path": "/cache-good?topic=beta",
"hx": false,
"restore": false,
"ifNoneMatch": "\"d4a2e4af8c3671d399ddcbbc92984556a75f795817fba6404ea84e1805892663\"",
"status": 200,
"representation": "full",
"vary": "HX-Request, HX-History-Restore-Request",
"etag": "\"843d827eaa297ed04d5424a03a78a7049356060ac71f73bc7e1b85d3b5691c41\""
},
{
"path": "/cache-good?topic=beta",
"hx": true,
"restore": true,
"ifNoneMatch": null,
"status": 200,
"representation": "full",
"vary": "HX-Request, HX-History-Restore-Request",
"etag": "\"843d827eaa297ed04d5424a03a78a7049356060ac71f73bc7e1b85d3b5691c41\""
}
],
"url": "about:blank",
"body": "",
"events": []
}
]
}

View file

@ -0,0 +1,47 @@
{
"name": "fep-navigation-history",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "fep-navigation-history",
"dependencies": {
"htmx.org": "2.0.11",
"playwright": "1.63.0"
}
},
"node_modules/htmx.org": {
"version": "2.0.11",
"resolved": "https://registry.npmjs.org/htmx.org/-/htmx.org-2.0.11.tgz",
"integrity": "sha512-Thx/WtpeOQqSrqBCw/A1cwGJGg4UrVa3+sW0GmrM3p4gJgO89ecH4qtbnyzDDWFvBTqjnIMCgELTNt636dtamA==",
"license": "0BSD"
},
"node_modules/playwright": {
"version": "1.63.0",
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.63.0.tgz",
"integrity": "sha512-+7ziBLidS4NaNCdt57SUDT+wYmmd5fmiQejUic/kb+YsYSCPyOOE9sebzMjNmQrsnNpDJqd4WHvV/8lfKfUDUg==",
"license": "Apache-2.0",
"dependencies": {
"playwright-core": "1.63.0"
},
"bin": {
"playwright": "cli.js"
},
"engines": {
"node": ">=20"
}
},
"node_modules/playwright-core": {
"version": "1.63.0",
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.63.0.tgz",
"integrity": "sha512-rYCsBF/M5HjUch52bbtVONEFjv6Xu8sm8h72dNlR5bzIE1fvC/bxgspzkjSfU+MweEMmPM8KJebG6nnyxo5mCg==",
"license": "Apache-2.0",
"bin": {
"playwright-core": "cli.js"
},
"engines": {
"node": ">=20"
}
}
}
}

View file

@ -0,0 +1 @@
{"name":"fep-navigation-history","private":true,"type":"module","scripts":{"test":"node experiment.mjs"},"dependencies":{"htmx.org":"2.0.11","playwright":"1.63.0"}}

File diff suppressed because it is too large Load diff