diff --git a/README.md b/README.md index 641dae1..4429f15 100644 --- a/README.md +++ b/README.md @@ -239,6 +239,12 @@ What the checks allow in this update: - check-art keeps every earlier assertion (whole bubble words, the bubble off her face, hands and the crate and off every opaque picture pixel, the strip clear of the text, no taller Vault title) at every width from 320 to 2560px in Chromium and WebKit, and adds: the Vault phone layout at every phone width; the live v14 title beside it (nothing in a title moves above 660px; on phones the Deposit and Redeem titles equal v14 and the Vault title differs only sideways as decided), with the v15 check rejecting the v14 look; and the focus marker at 660, 430, 393, 375, 360, 359 and 320px (the line on the marker's bottom band up to its outer edge, the v14 line put back is rejected, the line returns after blur, no shelf line at 661px). - The fork walk is unchanged in what it checks: all 91 v14 checks under the same names in the same order, none added, the same 70 page sends, the same 24 screenshot names at JPEG quality 18. check-motion, check-fixes, inspect-export, check-guards, check-preservation, check-interface, check-tokenomics, check-tokenomics-live and check-bask-guide are unchanged. +## Site update v20 + +The current patch is applied to v19, commit 8f88184b6dbcfd2c11f7a762c4c25cce55d8d230. It changes no source, word, picture or layout: the built site is byte-identical to v19. It repairs two browser checks. check-art and check-motion waited for a route to be shown by looking for its current-page navigation link (`a[aria-current="page"][href="#"]`); since v19 nothing links to the Losses page with that mark (the footer link went; the short-stock notice and the Docs panel link to it), so both checks timed out on #losses. They now count the Losses route as shown once its own heading "Losses" is the page title's; every other route still waits for its current navigation link, and nothing they assert changed. + +What the checks allow in this update: check-calldata freezes every web/src source whole against v19 (main.tsx and Docs.tsx included: v19's changes are HEAD's own), with every exception list empty; check-bundle allows no source change; check-visibility compares every Docs panel with the v19 site for equality again and asserts that "If a stock goes missing" stands right after Safety. + ## Site update v19 The current patch is applied to v18, commit ec5fa83ce47dd1a361cda55e086ef503d10f2deb. One thing, decided by the owner on 2026-10-15: how visitors reach the Losses page. A short stock is rare, but when one happens deposits stop until someone flags and recognises the shortfall, so the way in should show up where people look at that moment instead of sitting in the footer. diff --git a/web/scripts/check-art.mjs b/web/scripts/check-art.mjs index bdda5d0..ad419dc 100644 --- a/web/scripts/check-art.mjs +++ b/web/scripts/check-art.mjs @@ -377,7 +377,12 @@ function assertWideLabel(tag,label) { assert.deepEqual(tag.outside,[],label+': words inside pill');assert.equal(tag.overlap,0,label+': clear of character/bubble');assert.equal(tag.overflow,false,label+': no horizontal scroll'); } // A hash change re-renders asynchronously: wait until the requested route is the current page before measuring it. -const routeShown = name => !!document.querySelector(`a[aria-current="page"][href="#${name}"]`); +// v20: since v19 no navigation link points at the Losses page (the footer link went; the short-stock notice and the +// Docs panel link to it without a current-page mark), so that route counts as shown once its own heading "Losses" is +// the page title's. Every other route still waits for its current navigation link. +const routeShown = name => name === "losses" + ? location.hash === "#losses" && document.querySelector("main .page-title > h1")?.textContent.trim() === "Losses" + : !!document.querySelector(`a[aria-current="page"][href="#${name}"]`); function capacityGeometry() { const cells=[...document.querySelectorAll('.stats > div')],cell=cells[3],label=cell?.querySelector(':scope > span'),note=label?.querySelector('small'),value=cell?.querySelector(':scope > strong'); diff --git a/web/scripts/check-bundle.mjs b/web/scripts/check-bundle.mjs index 799b014..51bd876 100644 --- a/web/scripts/check-bundle.mjs +++ b/web/scripts/check-bundle.mjs @@ -6,9 +6,9 @@ import {createHash} from 'node:crypto'; const root=process.env.BASKET_SOURCE_ROOT||path.resolve('..'); const git=args=>execFileSync('git',args,{cwd:root,encoding:'utf8'}); const changed=git(['diff','--name-only','HEAD']).trim().split('\n').filter(Boolean); -// v19 (the owner's Losses decision, 2026-10-15: main.tsx drops the footer link and shows the short-stock notice, Docs.tsx gains the "If a stock goes missing" panel): only these two sources may change. +// v20 (two browser checks re-scoped for the Losses route, 2026-10-16): no source may change. // check-calldata independently pins each reviewed source and freezes every other. -const v19Source=['main.tsx','Docs.tsx'].map(f=>'web/src/'+f); +const v20Source=[]; const SHELL_SHA256='82f66899a0c19f61299254c0005ee5117af0a912b97e8e57d249288e9dbc962d'; const SHARE_SHA256='dc14dd6f4068ec9325426583104b1c6ae8748fbb17d8326863c9612280eacd85'; // The new favicon: red panel, yellow frame, the header's basket in yellow. @@ -23,10 +23,10 @@ for(const p of changed){ // v12 section 5: the exact page shell is pinned below; build configuration and validation fixtures stay unchanged. assert.ok(!['web/vite.config.ts','web/tsconfig.json'].includes(p)&&(!p.startsWith('web/validation/')||p==='web/validation/README.md'),'Page shell, build configuration or validation fixture changed: '+p); assert.ok(!/(^|\/)(lib|node_modules|\.github|\.git)(\/|$)|(^|\/)\.env(?:\.|$)|(^|\/)(foundry\.toml|foundry\.lock|remappings\.txt|\.gitmodules|package\.json|[^/]*lock[^/]*)$/.test(p),'Protected path changed: '+p); - if(p.startsWith('web/src/'))assert.ok(v19Source.includes(p),'Source outside the v19 scope changed: '+p); + if(p.startsWith('web/src/'))assert.ok(v20Source.includes(p),'v20 changes no source, but this changed: '+p); assert.ok(!p.startsWith('src/'),'Root contract source changed'); } -// v19: no new source and no new public asset. +// v20: no new source and no new public asset. const untracked=git(['ls-files','--others','--exclude-standard','--','web/src','web/public']).trim().split('\n').filter(Boolean); assert.deepEqual(untracked,[],'New files in web/src or web/public'); assert.equal(sha256('web/index.html'),SHELL_SHA256,'exact v12 source shell'); diff --git a/web/scripts/check-calldata.mjs b/web/scripts/check-calldata.mjs index c103bf4..afba253 100644 --- a/web/scripts/check-calldata.mjs +++ b/web/scripts/check-calldata.mjs @@ -11,14 +11,11 @@ const git=args=>execFileSync('git',args,{cwd:root,encoding:'utf8'}); const baseline=p=>git(['show','HEAD:'+p]); const read=p=>fs.readFileSync(path.join(root,p),'utf8'); const sha256=s=>createHash('sha256').update(s).digest('hex'); -// v19 on HEAD=v18: one thing. The owner's decision on the Losses page (2026-10-15): the footer no longer links to it. -// Instead main.tsx shows a notice above the Vault and Deposit pages while an unretired stock is short ("Deposits are -// paused: the vault is short of ..."), whose link opens the Losses page, and Docs.tsx gains the panel "If a stock goes -// missing" that explains the flag, the 7-day wait and the recognise step, with the same link. The Losses page and its -// route are HEAD's. The notice reads only the snapshot App already holds. No call that acts, attribute that acts, -// import, state, storage use, wallet request or chain read is added. Every other web/src file is byte-identical to -// HEAD, styles.css included. Transactions remain HEAD's: send(), connect(), disconnect() and switchChain() keep their -// pins. +// v20 on HEAD=v19: no source changes. The update re-scopes two browser checks (check-art, check-motion) whose wait for +// a route needed a current-page navigation link, which the Losses page no longer has since v19, and returns this +// gate, check-bundle and check-visibility to "equal to HEAD": v19's documented changes in main.tsx and Docs.tsx are +// HEAD's own now. Every web/src file is byte-identical to HEAD, styles.css included. Transactions remain HEAD's: +// send(), connect(), disconnect() and switchChain() keep their pins. const protectedFiles=[ "BaskGuide.tsx", "Flows.tsx", @@ -30,6 +27,8 @@ const protectedFiles=[ "Tokenomics.tsx", "tokenomicsData.ts", "wallet.tsx", + "main.tsx", + "Docs.tsx", "baskGuideData.ts", "chain.ts", "deployment.ts", @@ -67,30 +66,14 @@ assert.equal(sha256(fs.readFileSync(path.join(root,'web/src/styles.css')).subarr // v16: no style rule after HEAD's text (the v15 rules are HEAD's own now). const cssTail=fs.readFileSync(path.join(root,'web/src/styles.css')).subarray(Buffer.byteLength(baseline('web/src/styles.css'))).toString('utf8'); assert.equal(cssTail,'','v16: no style rule after the text of HEAD'); -// The v19 files: byte-identical to HEAD once their documented pieces [v19, HEAD] are put back (each exactly once), so +// The v20 files (none: v20 changes no source): byte-identical to HEAD once their documented pieces [v20, HEAD] are put back (each exactly once), so // every word, call and attribute in them is pinned. The list between the two marker lines was generated from the // difference to HEAD; the pins further down do not depend on it: they compare with HEAD itself or with texts written // out in this file, so a piece cannot carry a change to a transaction, a wallet request or a chain read past them. -// >>> v19 pieces (generated from the difference to HEAD, not edited by hand) -const editedFiles={ - "main.tsx":[ - [" }, []);\n // Unretired stocks the vault holds less of than it manages: while any is\n // short, the vault refuses deposits.\n const shortStocks = snapshot.assets.filter((a) => a.short && !a.retired);\n return (\n", - " }, []);\n return (\n"], - [" )}\n {(page === \"Vault\" || page === \"Deposit\") && shortStocks.length > 0 && (\n
\n Deposits are paused: the vault is short of{\" \"}\n {shortStocks.map((a) => a.symbol).join(\", \")}.{\" \"}\n Anyone can start the fix.\n
\n )}\n {page === \"Vault\" ? (\n", - " )}\n {page === \"Vault\" ? (\n"], - [" \n Docs\n", - " \n \n Losses\n \n Docs\n"] - ], - "Docs.tsx":[ - [" \n
\n

If a stock goes missing

\n

\n The vault keeps its own count of each stock it holds. If the real\n balance ever falls below that count, for example because the issuer\n burned or took back tokens, that stock is short. While an unretired\n stock is short, deposits are paused. Redemptions keep working and\n pay from the real balance.\n

\n

\n Anyone can correct the count on the{\" \"}\n Losses page, in two steps. Flag shortfall\n records the gap and starts a 7-day wait. After the wait, Recognise\n loss lowers the count by the recorded gap or the current gap,\n whichever is smaller, and deposits can open again. If the tokens\n come back during the wait, less or nothing is written off. If the\n gap grows, flagging again records the larger gap and starts a new\n 7-day wait.\n

\n

\n This only corrects the records: the missing tokens were already\n gone, and redemptions were already paying from what is really there.\n

\n
\n
\n", - "
\n
\n"] - ] -}; -// <<< v19 pieces -assert.deepEqual(Object.keys(editedFiles),[ - "main.tsx", - "Docs.tsx" -],'the v19 files'); +// >>> v20 pieces (generated from the difference to HEAD, not edited by hand) +const editedFiles={}; +// <<< v20 pieces +assert.deepEqual(Object.keys(editedFiles),[],'v20 edits no source'); const putBack=(text,pieces,what)=>{for(const [n,o] of pieces){assert.equal(text.split(n).length,2,what+': expected once: '+n);text=text.split(n).join(o);}return text;}; for(const [f,edits] of Object.entries(editedFiles)) assert.equal(putBack(read('web/src/'+f),edits,f),baseline('web/src/'+f),f+' changed beyond the documented pieces'); // Calls that build, encode, simulate or send a transaction (or parse its arguments) or read the chain. @@ -144,17 +127,17 @@ function difference(a,b){ } // Guarded calls: the exact, documented differences from HEAD, each added call with the place it must sit in // (everything equals HEAD, in order and place). The existing receipt read and display-only amount parser are -// already part of HEAD (v18's addToWallet() calls included). v19 adds no guarded call, wallet request or chain read. +// already part of HEAD (v18's addToWallet() calls included). v20 adds no guarded call, wallet request or chain read. const expected={}; // Action attributes: the exact differences from HEAD, in page order, each [element, attribute, why]. Everything else // equals HEAD in order, text for text. None of them builds or starts a transaction (no onSubmit, getSpec, build or // claim) and none contains a guarded call (both asserted below). The list between the marker lines was generated // from the difference to HEAD, as the pieces were; each why was written by hand, and an entry without one fails. -// >>> v19 attributes (generated from the difference to HEAD; each why written by hand) +// >>> v20 attributes (generated from the difference to HEAD; each why written by hand) const attributeChanges={ }; -// <<< v19 attributes +// <<< v20 attributes // Every call expression: HEAD's calls, in order and place, are all still there, except the removed ones listed here; // a call that only gained the documented piece inside it (put back before comparing) counts as kept. Every added // call, including display helpers and effects, is independently pinned below, not only the guarded calls. @@ -167,7 +150,7 @@ const attributeChanges={ // The guarded startup eth_accounts answer is part of HEAD and must remain exact. const restoreAccountReply=(x)=>x.text; // Reviewed v16 differences in calls and watched-name uses. Independent of the generated pieces. -// v19: no call of HEAD is removed and no watched name gains a use (v18's two setMessage uses are HEAD's own now). +// v20: no call of HEAD is removed and no watched name gains a use. const callChanges={}; const WATCHED=[...names].filter(x=>!x.includes('.')).concat('client'); // The state setters and refs a file declares: const [x, setX] = useState(...) gives setX, const r = useRef(...) gives r. @@ -219,7 +202,7 @@ function localDeclarations(file,text,fn,list){ const sources=git(['ls-tree','-r','--name-only','HEAD','web/src']).trim().split('\n').filter(p=>/\.tsx?$/.test(p)).map(p=>p.slice(8)); assert.deepEqual(git(['ls-files','--cached','--others','--exclude-standard','web/src']).trim().split('\n').filter(p=>/\.tsx?$/.test(p)).map(p=>p.slice(8)).sort(),[...sources,...NEW_SOURCES].sort(),'No source file added or removed'); // Every source is either frozen (byte-identical to HEAD) or a v16 file with its documented pieces (above). -assert.deepEqual([...sources].sort(),[...protectedFiles,...Object.keys(editedFiles)].filter(f=>/\.tsx?$/.test(f)).sort(),'every web/src source is frozen or a documented v19 file'); +assert.deepEqual([...sources].sort(),[...protectedFiles,...Object.keys(editedFiles)].filter(f=>/\.tsx?$/.test(f)).sort(),'every web/src source is frozen (v20 edits none)'); const scans={},facts={attributes:{},guarded:{},calls:{}}; for(const f of sources){ const old=calls(baseline('web/src/'+f),f),now=calls(read('web/src/'+f),f),c=callChanges[f]??{pieces:[],removed:[],uses:[]},used=c.pieces.map(()=>0); @@ -242,16 +225,7 @@ if(process.env.BASKET_GATE_FACTS)fs.writeFileSync(process.env.BASKET_GATE_FACTS, // ordered callee names make the list readable without repeating large nested JSX bodies. No regeneration block // includes these pins. Changing a callback, receiver, argument, location, order or count changes the digest. // >>> reviewed calls (manual; never refreshed by put-pieces) -const addedCallPins={ - "main.tsx": { - "sha256": "8be0ad9ef44faa5f10e806635279f9376403fcff9003562793ddd4028a9994a2", - "callees": "filter join map" - }, - "Docs.tsx": { - "sha256": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945", - "callees": "" - } -}; +const addedCallPins={}; // <<< reviewed calls // The exact reviewed source of the two v14 files (frozen now) and of the two v16 files, beyond either generated block. const reviewedSourcePins={ @@ -394,7 +368,7 @@ function sendsOf(text,file){ })(tree); return out; } -// v19 adds no provider use: every file's list equals HEAD's (v18's addToWallet() uses included). +// v20 adds no provider use: every file's list equals HEAD's. const sendAdded={}; const NO_SEND=['Vault.tsx','Scenery.tsx','main.tsx','Docs.tsx'],sendUses={}; for(const f of sources){ @@ -436,24 +410,10 @@ function loadsOf(text,file){ })(tree); return out; } -// v19: one link moves in main.tsx and one is added in Docs.tsx, each written out here with its place. The Losses link -// (href="#losses") leaves the footer (removed once from HEAD's list) and is the link of the short-stock notice above -// the Vault and Deposit pages (added once, App); the Docs panel "If a stock goes missing" links to the same page -// (DocsPage). Nothing else a page loads or opens changes in any file: HEAD's list without the removed entry stays -// complete, in order and in place, and the only additions are these. -const loadDisplayAdded={ - "main.tsx": [ - {"place": "App", "text": "href=\"#losses\""} - ], - "Docs.tsx": [ - {"place": "DocsPage", "text": "href=\"#losses\""} - ] -}; -const loadDisplayRemoved={ - "main.tsx": [ - {"place": "App", "text": "href=\"#losses\""} - ] -}; +// v20 adds, removes and moves nothing a page loads or opens: every file's list equals HEAD's (v19's Losses link move +// and Docs link included), in order and in place. +const loadDisplayAdded={}; +const loadDisplayRemoved={}; const loadUses={}; for(const f of sources){ const h=loadsOf(baseline('web/src/'+f),f),n=loadsOf(read('web/src/'+f),f),added=loadDisplayAdded[f]??[],removed=loadDisplayRemoved[f]??[]; @@ -469,7 +429,7 @@ for(const f of sources){ } // v12: the wallet and RPC methods the pages name. Every `method` given as a property (or assigned) in web/src, in order // with its place: exactly HEAD's list. Local-only Disconnect names no new wallet or RPC method. -// v19 names no new wallet or RPC method in any file (v18's three in addToWallet() are HEAD's own). +// v20 names no new wallet or RPC method in any file. const methodsAdded={}; function methodsOf(text,file){ const tree=parse(text,file),print=printerOf(tree),out=[],key=n=>ts.isIdentifier(n)||ts.isStringLiteralLike(n)?n.text:ts.isComputedPropertyName(n)?lit(n.expression):undefined; @@ -586,7 +546,7 @@ function hookReturn(text){ assert.equal(returns.length,1,'wallet.tsx useWallet: one return'); return print(returns[0]); } -// v19: the hook's return list is HEAD's (v18's, with addToWallet after switchChain), pinned here printed without +// v20: the hook's return list is HEAD's (with addToWallet after switchChain since v18), pinned here printed without // comments; nothing in the list may change. const HOOK_RETURN_HEAD="return { account, connecting, progress, report, fail, notice, listingPending, waitPending, chainId, message, hash, busy, confirmed, connect, disconnect, switchChain, addToWallet, send, setMessage, };"; assert.equal(hookReturn(baseline('web/src/wallet.tsx')),HOOK_RETURN_HEAD,'wallet.tsx: HEAD\'s hook return list as pinned'); @@ -698,10 +658,8 @@ for(const f of Object.keys(editedFiles)){ assert.deepEqual(n,h,f+': a name used by the transaction code is declared around it differently from HEAD'); transactionCode[f]={pieces:n.count,namesDeclaredAroundThem:n.names}; } -// v19 adds no name at the top of a file. +// v20 adds no name at the top of a file. const namesAdded={ - "main.tsx": {}, - "Docs.tsx": {} }; // v16: the exact declarations that change at the top of the two files (two imports leave Tokenomics.tsx; four data // declarations of tokenomicsData.ts carry the reviewed words); never generated by put-pieces. @@ -917,7 +875,7 @@ fs.writeFileSync('../artifacts/calldata-comparison.json',JSON.stringify({result: 'Direct and aliased transaction-state writes equal HEAD; send-tail receipt-hash writes are individually pinned with their ordered statements', 'Wallet send head is byte-identical to HEAD through the receipt wait; its tail equals HEAD after exact receipt-hash replacements; connect and disconnect equal reviewed text and the new counter is used only at the pinned sites', 'All protected wallet functions, flow effects, transaction buttons and handlers are byte-identical to HEAD; declaration and wrapper-scope checks prevent name rebinding', - 'Provider methods and uses, browser storage and network sinks equal HEAD; page loads equal HEAD apart from the documented Losses link move and the one Docs link; Disconnect makes no request; components retains the same single wallet.send', + 'Provider methods and uses, browser storage and network sinks equal HEAD; page loads equal HEAD; Disconnect makes no request; components retains the same single wallet.send', 'The differential encoding corpus, six-clock send builders, parser declarations, pinned v9 decisions and imported bindings retain every prior assertion', 'Only approved v12 source files change; shell and share-card bytes are separately pinned by check-bundle.mjs' ]},null,2)+'\n'); diff --git a/web/scripts/check-motion.mjs b/web/scripts/check-motion.mjs index 6a1e7de..70208ac 100644 --- a/web/scripts/check-motion.mjs +++ b/web/scripts/check-motion.mjs @@ -50,8 +50,16 @@ try { ]) { await page.goto(url + "#" + name); // A hash change re-renders asynchronously: wait until the requested route is the current page. + // v20: no navigation link points at the Losses page since v19, so that route counts as shown once its own + // heading "Losses" is the page title's; every other route still waits for its current navigation link. await page.waitForFunction( - (n) => !!document.querySelector(`a[aria-current="page"][href="#${n}"]`), + (n) => + n === "losses" + ? location.hash === "#losses" && + document + .querySelector("main .page-title > h1") + ?.textContent.trim() === "Losses" + : !!document.querySelector(`a[aria-current="page"][href="#${n}"]`), name, ); await page.evaluate(() => document.fonts.ready); diff --git a/web/scripts/check-visibility.mjs b/web/scripts/check-visibility.mjs index 88e21e8..c49cf59 100644 --- a/web/scripts/check-visibility.mjs +++ b/web/scripts/check-visibility.mjs @@ -353,19 +353,13 @@ try { assert.deepEqual(oldLayout.docsParagraphs.slice(1).map(p=>p.text),docsAdded,'Baseline carries the two v12 paragraphs and the live fee sentence'); assert.deepEqual(rounded(newLayout.docsParagraphs),rounded(oldLayout.docsParagraphs),'The Docs paragraphs equal the baseline'); assert.deepEqual(newLayout.docsTitles.slice(-7),['Risks',...ITEM_PANELS,'Find Basket'],'The five ITEM panels sit between Risks and Find Basket'); - // v19 (the owner, 2026-10-15): one panel, "If a stock goes missing", stands right after Safety. Every panel - // before it equals the baseline in place and size; every panel after it keeps its size and moves down by one and - // the same distance: the new panel's height plus the grid spacing above it. - const V19_PANEL='If a stock goes missing',at=oldLayout.docsTitles.indexOf('Safety')+1; - assert.ok(at>0&&!oldLayout.docsTitles.includes(V19_PANEL),'Baseline has Safety and not the v19 panel'); - assert.deepEqual(newLayout.docsTitles,[...oldLayout.docsTitles.slice(0,at),V19_PANEL,...oldLayout.docsTitles.slice(at)],'The Docs panels equal the baseline, in order, with the v19 panel after Safety'); - assert.deepEqual(rounded(newLayout.docs.slice(0,at)),rounded(oldLayout.docs.slice(0,at)),'Every Docs panel before the v19 panel keeps its place and size'); - const added=newLayout.docs[at],shift=added.height+(added.top-newLayout.docs[at-1].bottom); - assert.ok(added.height>0&&shift>added.height,'The v19 panel is rendered below Safety with the grid spacing'); - assert.equal(newLayout.docs.length,oldLayout.docs.length+1,'One Docs panel added'); - oldLayout.docs.slice(at).forEach((d,i)=>{const n=newLayout.docs[at+1+i];assert.ok(near(n.height,d.height)&&near(n.top,d.top+shift)&&near(n.bottom,d.bottom+shift),'Docs panel '+oldLayout.docsTitles[at+i]+' keeps its size and moves down by the v19 panel: '+JSON.stringify({old:d,now:n,shift}));}); + // v20: the v19 panel "If a stock goes missing" is part of the baseline now, right after Safety; every Docs panel + // equals the baseline in order, place and size again. + assert.equal(newLayout.docsTitles[newLayout.docsTitles.indexOf('Safety')+1],'If a stock goes missing','The v19 panel stands right after Safety'); + assert.deepEqual(newLayout.docsTitles,oldLayout.docsTitles,'The Docs panels equal the baseline, in order'); + assert.deepEqual(rounded(newLayout.docs),rounded(oldLayout.docs),'Every Docs panel keeps its place and size'); for(const d of newLayout.docs)assert.ok(d.height>0,'Docs panel rendered'); - checks.push('Docs: the four paragraphs with the live fee sentence, the five ITEM panels between Risks and Find Basket; the v19 panel after Safety; every panel before it equals the baseline in place and size, every panel after it keeps its size and moves down by the v19 panel'); + checks.push('Docs: the four paragraphs with the live fee sentence, the five ITEM panels between Risks and Find Basket, "If a stock goes missing" after Safety; every panel equals the baseline in place and size'); } if(name==='redeem') { assert.equal(oldLayout.redeemAside,null); diff --git a/web/validation/README.md b/web/validation/README.md index 564154e..5bbcf4a 100644 --- a/web/validation/README.md +++ b/web/validation/README.md @@ -53,6 +53,8 @@ The v14 baseline is the v13 commit 2b7d55bc32d09610fed936d963d9a8c492f4eecf. che The v15 baseline is the v14 commit c05c174f639d352fb6184636bcc6e083031daa65. v15 changes style rules only (the end of styles.css): check-calldata has no edited script file, freezes every source whole against v14, keeps the module and source digests and pins the added style rules by one reviewed digest with a scan; check-bundle's source scope is styles.css alone with no new file. check-visibility compares the v14 six-route phone view at 375×812 with no exception: every first-screen element keeps its words and vertical place, the Deposit and Redeem titles equal v14 across the panel, and the Vault title is asserted across the panel (picture in the middle, shelf line across the panel, heading centred) against the v14 look. check-art keeps its assertions at every width from 320 to 2560px in both engines and adds the Vault phone layout, the comparison of every title with the live v14 title, and the heading's focus marker meeting the shelf line on Deposit and Redeem at seven phone widths, each with the v14 look rejected by the same check. The v15 fork keeps all 91 v14 checks under their names in order with none added, the same 70 page sends and the same 24 screenshots at JPEG quality 18. +The v20 baseline is the v19 commit 8f88184b6dbcfd2c11f7a762c4c25cce55d8d230. v20 changes no source; check-art and check-motion count the Losses route as shown by its heading "Losses" (no navigation link carries its current-page mark since v19), and check-calldata, check-bundle and check-visibility compare with v19 for equality. + The v19 baseline is the v18 commit ec5fa83ce47dd1a361cda55e086ef503d10f2deb. v19 changes two display sources (main.tsx: the footer's Losses link removed, the short-stock notice above the Vault and Deposit pages added; Docs.tsx: the "If a stock goes missing" panel) and nothing else; check-calldata freezes every other source, wallet.tsx included, pins the two by digest and by generated pieces, pins the notice's three display calls, and lists the Losses link's move and the one Docs link as the only changes to what a page opens; check-visibility expects the one new Docs panel after Safety (panels before it equal the baseline, panels after it keep their size and move down by it). No transaction, wallet request, chain read or storage change. The v18 baseline is the v17 commit. v18 changes three sources (wallet.tsx: useWallet gains addToWallet(), one EIP-747 wallet_watchAsset request behind two eth_chainId reads and the existing switchChain(); main.tsx: hands it to the Docs page; Docs.tsx: the Add BASK to MetaMask button and its sentence) and nothing else; check-calldata freezes every other source, pins the three by digest and by generated pieces, and writes out every added call, provider use, method, attribute, watched-name use and declaration with its place, the hook's return list included. No transaction, storage or page-load change; check-bundle's source scope is the three files.