{"assessments":[],"deployments":[],"fuzz":[],"identity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"interpretation":"Records acceptance and evidence. Neither completion nor an AI assessment establishes correctness, safety, or independent review.","jobId":"d89b2921-8c80-4361-bd23-f03cdc8eae4f","kind":"shape:chain","nodes":[{"acceptedSubmissionHash":"add2ffd0295ed17024cd07cef0d794ba6f0cf718683ad0a07491d73029d1c502","dependsOn":["frontend_for_contract","site_content_check"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"6b037a7b6601e883cf8a906c1520c0624817d42d8310b65c2f43679204608af3","skillId":"adversarial-review","tools":[]},"key":"adversarial_review","kind":"code","role":"review","skillHash":"6b037a7b6601e883cf8a906c1520c0624817d42d8310b65c2f43679204608af3","skillId":"adversarial-review","state":"accepted"},{"acceptedSubmissionHash":"9c738a1073159ccafccc36bf34178a7a7f66f526d6572c4875d6705f1c1ef802","dependsOn":["frontend_for_contract"],"execution":{"network":true,"optionalTools":["browser"],"profile":"none","requires":["network"],"skillHash":"4a5e9fb083a47bea78572ce4262f6ed18e7af57736a86ec2bbb7d17a8c72c62e","skillId":"design-critic","tools":[]},"key":"design_critic","kind":"code","role":"review","skillHash":"4a5e9fb083a47bea78572ce4262f6ed18e7af57736a86ec2bbb7d17a8c72c62e","skillId":"design-critic","state":"accepted"},{"acceptedSubmissionHash":"9bafe6b70c7f9ca1f1c8cd3dd93a258c10bb337a6ba5d977f618df759d3789e4","dependsOn":[],"execution":{"mustProduce":["dist/index.html"],"network":true,"optionalTools":["browser"],"profile":"none","requires":["network"],"skillHash":"ed66ac168d7374db08ca358b6745195f339b0c74331416d3620ee268632b790c","skillId":"frontend-for-contract","tools":[]},"key":"frontend_for_contract","kind":"code","role":"implement","skillHash":"ed66ac168d7374db08ca358b6745195f339b0c74331416d3620ee268632b790c","skillId":"frontend-for-contract","state":"accepted"},{"acceptedSubmissionHash":"2fc5493b6b94610a2fa40a960012bd71a6c3802d8772af0f3b9bf8c3a2e072b4","dependsOn":["frontend_for_contract"],"execution":{"network":false,"profile":"foundry","requires":[],"skillHash":"8189a3059fc9bb774ad7dcee66b87f32e25d781025f0e9f82a4c75182fe0ef19","skillId":"site-content-check","tools":[]},"key":"site_content_check","kind":"code","role":"review","skillHash":"8189a3059fc9bb774ad7dcee66b87f32e25d781025f0e9f82a4c75182fe0ef19","skillId":"site-content-check","state":"accepted"}],"objective":"SITE v2 for Marginalia (continue the site delivered by job 1a751f97; keep its stack, its ledger look, its runtime manifest, its transaction builders and its review posture; change what is listed here). THE JOB OF THIS VERSION: give readers and writers a reason, not a form. Readers come to find out who is actually right; writers come to prove they are. Every page should answer one of those two. AUDIENCE (design and copy are judged against this): crypto Twitter. Two kinds of visitor, in this proportion. (1) READERS, nine in ten: they follow traders and callers on X, read on a phone between tweets, lurk rather than post, screenshot anything they want to keep, and are skeptical of every call because they have been shilled before. They arrive from a shared card or a link in a bio and decide in seconds whether this is worth a bookmark. What they want is to know who is actually right and to not be dumped on; the leaderboard, the verdicts strip, the hold rule and the share cards are for them. (2) WRITERS, one in ten: people who already post calls and targets publicly for free, competitive, proud of their hit rate, wary of showing losses; and increasingly AI agents posting from contract wallets. They want a score nobody else can give them, a public duel they can win, a weekly prize, and later readers who pay them; the guided first note, the difficulty-weighted score, the duel button and the prize clock are for them. Neither group reads documentation. Both know what a bond, a horizon and a settlement are; explain the mechanics in one line each, never in a paragraph. Voice: dry, specific, no slang, no exclamation marks, no 'degen', no promises of returns; the tone of a good ledger. 1. HOME is a scoreboard, not a form. Above the fold, in this order: (a) THE PRIZE AND THE CLOCK: 'This week's thesis prize: N IMD (≈$ at the live IMD price)' read from hook.thesisPool(week()) with the judging-opens time and the count of eligible theses (judge.eligibleTheses(week).length); if zero, say 'no thesis entered yet' as an invitation; (b) THE LEADERBOARD: authors ranked by records(address).score with posted/right/wrong/broken/retracted and a hit rate, built from NotePosted authors (the history loader already indexes them); tabs for 'by score' and 'by hit rate'; (c) OPEN DUELS: pairs of notes where one note's parentId is another author's note on the same X from the opposite side; show both authors, both claims, both bonds and the horizon side by side; (d) THE FEED, with a 'recent verdicts' strip (Settled events) above it; (e) three reasons in plain words, one line each: 'Receipts for every call: a price, a deadline, a bond, and the chain's verdict' · 'Prove it: a difficulty-weighted score, public duels, a weekly prize' · 'Readers pay writers: pages of an author's marginalia (coming with the page market)'. 2. RECEIPTS INDEX. Convention: a note whose text begins with an x.com or twitter.com status URL is a receipt of someone else's public call; the site parses the handle from the URL, renders 'cites @handle' on the card and the note page, and adds a CITED ACCOUNTS leaderboard (by the verdicts of the notes that cite them) beside the author leaderboard; the note's author stays the posting wallet; copy must say the cited account did not post the note. 3. DUEL PAGE: /duel/<id> renders a counter-call against its target: both notes, both records, settlement state of each, who was right. Posting a counter from a note page: a 'Counter this call' button that opens the post form with parentId, X and the opposite side prefilled. 4. AUTHOR PAGE: record as a scoreboard (score, hit rate, difficulty-weighted wins, broken-hold count stated plainly), the author's notes, open duels, and a 'Record of a wallet, claimable later' note for unclaimed wallets. 5. SHARE: every note, duel and author page has a 'Share' control that renders a 1200x630 card (canvas, client-side; the ledger look, the claim, the verdict or the clock, the author short address) and copies the image plus a text line to the clipboard, with a fallback download. The site has no backend; an unfurling OG image comes later from a separate service. 6. GUIDED FIRST NOTE: a three-step first-post flow on /post for a wallet with no notes: pick a token (venue finder), pick a side and a target from presets (+25% / +50% / 2x / -25%), say why in one line; the cost shown in IMD and in dollars at the live IMD price; the checkpoint wait explained in one sentence with a countdown. 7. SEALING ENABLED: the hook's seal(x, venue, hash) pulls the 0.1 IMD stamp from the author by transferFrom, so the seal page may request ONE additional approval: IMD to the HOOK address (hook address from the runtime manifest) for exactly STAMP = 0.1 IMD, and nothing else; the router approval rule otherwise stands. Show the seal→reveal timeline (30 min to 6 h) and keep the sealed text with the author (local draft) until reveal. 8. COPY: the three reasons above; the hold rule stated honestly on the post page and the about page: 'An upward call's wallet must hold through the horizon or forfeit its bond; this binds the posting wallet, not every wallet the author owns'. 'Say it with a bond.' as the home tagline. Keep: informational, not advice; bonds can be lost; no 'buy'. 9. PERMISSIONLESS BUTTONS stay (observe / settle / forfeit) and the pools page stays; move the pools summary onto home as the prize line.  10. AGENT DOCS: serve the repository's llms.txt at /llms.txt and AGENTS.md at /agents.md (copied verbatim into the static export; link both from the About page under 'For agents'), so an LLM agent can read how to post without the UI. OUT OF SCOPE for v2: the page market pages (next continue, once MarginaliaPages is deployed). REVIEW BRIEF for the review steps, unchanged from v1 plus: the hook approval on the seal page must be exactly STAMP and only there; the share card must render client-side with no network call; the receipts parser must never treat a cited handle as the author. Any address, approval or script finding is critical and must refuse hosting.","parentJobId":"1a751f97-4657-4c3b-989c-cb1f3ab02c66","planHash":"50cb1ae4c73c7e9f17a905abdad0385187c1bc88f3a5e617f2b69468600a333a","previousHash":"23f2e18471b983f96adb619666bb9a77ec2f923ba6e93512660076d2d455891c","projectId":"9060a248-29ce-4683-b5d1-54fbc83cd49d","publication":{"commit":"6b84a94e849c4c98cd1591bc56bed56c31ef9ff0","deliveredAt":"2026-10-12T05:26:40.000Z","repoUrl":"https://github.com/identity-md-launches/launch-1283-marginalia-x-com-marginalia-md-notes-mar"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[],"site":{"cid":"bafybeifv5n75rz6glj73ukpalbqvezzmmieex4q6avqalqt6rxak54r7sy","ensName":"marginalia.site.identitymd.eth","namingTransaction":"0xeb46c64cbcdba29635e2affa0024991abbe9509781e7455771b3e8c2c450df85"},"snapshotHash":"8d08fff60e894faf4a903726e03c8c3b8e3bcf99ab80605eb72b93aff8a7cf65","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":null,"device":"4dd9389cdce7acfe","findings":[{"citation":"resolved","description":"The hosted bundle dist/assets/index-Dx7Om2au.js contains this hashchange handler. The skip link changes the URL to #main, while this early return keeps the note, duel or author page displayed. Share in web/src/share.tsx reads window.location.href, so after the next clock render it copies #main instead of the displayed record route. Opening that copied URL or reloading resolves to the home scoreboard, losing the note/duel/author destination. This is a navigation/sharing defect, not a hosting blocker.","line":22,"path":"web/src/App.tsx","reproduction":"With a loaded note, open #/note/0x1. Focus the Skip to content link and press Enter. Wait at least one second for the clock render, then press Share. Expected: the clipboard text and Share text field retain #/note/0x1. Actual, reproduced against dist with mocked RPC data: the displayed Note № 1 stays open but Share text ends in /#main. Open that link in a fresh tab or reload: the scoreboard appears instead of Note № 1. The same hash handler affects duel and author routes.","severity":"low","snippet":"      if (window.location.hash === '#main') { document.getElementById('main')?.focus(); return; }","title":"Skip-to-content replaces the page route and makes Share copy a scoreboard link"}],"hash":"2fc5493b6b94610a2fa40a960012bd71a6c3802d8772af0f3b9bf8c3a2e072b4","nodeId":"133e241b-b708-425c-a7ce-334989940714","outcome":"completed","summary":"Review complete. Recorded one low-severity defect in [.imd-findings.json](.imd-findings.json): “Skip to content” causes Share to copy a scoreboard link instead of the displayed record’s URL.\n\nNo critical or high hosting blockers found. Export hashes, ABI checks, live read-only verification, unit tests, and 21 browser scenarios passed. Site files unchanged.","treeHash":null,"usage":{"cachedInputTokens":2740864,"inputTokens":137295,"model":"gpt-6.1-sol","outputTokens":8201,"runtime":"codex","turns":6,"wallClockMs":356980}},{"artifacts":[],"attempt":1,"bundleHash":"0bbb2c38878e8236c5340f2a8f02860e15fe2cf854723ca230223509ce1d2948","device":"51908b9b0306f441","findings":[],"hash":"9bafe6b70c7f9ca1f1c8cd3dd93a258c10bb337a6ba5d977f618df759d3789e4","nodeId":"d0dc5eb8-8a0b-4f9b-80bf-d6d8768295ba","outcome":"completed","summary":"Implemented Marginalia v2 and rebuilt `dist/`.\n\n- Scoreboard, receipts, duels, sharing, guided posting, and sealing.\n- Build, typecheck, 21 browser scenarios, and 21 unit/history checks passed.\n- Manifest, asset hashes, and 8 MiB limit verified.\n- Design and validation documented.\n\nUSD displays use the permitted unavailable fallback; no verified price source was supplied. No live transactions were sent.","treeHash":"8e0cb9e5b56c34b6830863349c073a0afbe16762","usage":{"cachedInputTokens":4725504,"inputTokens":195791,"model":"gpt-6-astra","outputTokens":58807,"runtime":"codex","turns":10,"wallClockMs":1452701}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"593a9c894818e368","findings":[{"citation":"resolved","description":"Now: UsdContext always renders a fixed unavailable label; it accepts no IMD amount and reads no price. Home prize, posting total and seal cost therefore never show dollar context.\nChange to: Supply an explicitly sourced, timestamped IMD/USD quote to this shared component and multiply it by the displayed IMD amount, retaining the unavailable label only for absent or stale quotes.\nWhy: Requirements 1(a) and 6 explicitly ask for the prize and posting cost in dollars at the live IMD price. The missing deployment oracle justifies the honest fallback, not marking that requirement delivered.","line":3,"path":"web/src/price.tsx","reproduction":"At 390 or 1440 px, load #/ and #/post with successful Ethereum reads. The prize and 0.6 IMD initial posting total each render USD context unavailable. Changing the bond or prize cannot produce a dollar value: web/src/price.tsx has no props, price read or conversion, and both pages invoke <UsdContext /> without an amount. Expected a live, sourced conversion for each amount.","severity":"high","snippet":"  return <span className=\"small\" title=\"The deployment supplies no verified IMD/USD price source.\">USD context unavailable</span>;","title":"fidelity: live dollar context is absent from the prize and posting cost"},{"citation":"resolved","description":"Now: The separate masthead, wallet strip, kicker, tagline, CTA and tall stacked prize consume the first phone screen before any ranking or result.\nChange to: Keep the existing wallet control beside the brand, remove the redundant The public record of a call kicker, reduce the home heading spacing, and compact the mobile prize and clock so the leaderboard tabs and first record (or empty-state invitation) fit within 844 px. Keep the prize first and the existing ledger styling.\nWhy: The brief makes nine in ten visitors readers and explicitly requires a scoreboard above the fold; the current first screen gives them no record to compare.","line":89,"path":"web/src/pages/Home.tsx","reproduction":"At 390×844, open #/ in a fresh browser without a wallet and wait for the Ethereum reads to finish. taste@1 measured #author-leaderboard at y=838.36 with height 26.5; Posting wallets begins at y=881.86 and the ranking controls and entries are below that. The screenshot shows only the header, wallet strip, slogan, posting CTA, prize and history count. Expected at least the scoreboard controls and a result or its empty state on the first screen. An independent populated-state run with four notes and two authors put the first ranking row at y=963.23, Open duels at y=1589.06, and Recent verdicts at y=2167.75.","severity":"high","snippet":"  return <><div className=\"home-heading\"><div><p className=\"eyebrow\">The public record of a call</p><h1>Say it with a bond.</h1></div><a className=\"button\" href=\"#/post\">Put a call on record →</a></div>","title":"fidelity: the phone first screen hides the reader scoreboard"},{"citation":"resolved","description":"Now: Ranking rows are a non-wrapping flex line. In WebKit at 200% text size, the rank, cited handle and result compete for one row; the result wraps into individual characters beyond the viewport and creates horizontal scrolling.\nChange to: Add flex-wrap: wrap to .rank-line so the result can move to another line while the handle and count remain readable. Preserve the labels and values.\nWhy: Readers must be able to enlarge the ledger on their phones without losing its comparison data. This is an isolated accessibility/reflow failure, not a change to the ledger style.","line":193,"path":"web/src/styles.css","reproduction":"In WebKit, set viewport to 390×844, open #/ with the existing gallery fixture containing a receipt for @market_reader (one right result), then set document.documentElement.style.fontSize to 34px, twice the shipped 17px. Scroll to Cited accounts. document.documentElement.scrollWidth is 417px for a 390px viewport; the 1 right result starts at x=389.86 and ends at x=417.05, and the row grows to 336px tall with vertical character wrapping. In the same browser session, injecting .rank-line { flex-wrap: wrap; } restores scrollWidth to 390 and readable rows. Normal-size 320/390/768/1440 layouts passed.","severity":"medium","snippet":".rank-line { display: flex; align-items: baseline; gap: 0.75rem; margin-bottom: 0.25rem; }","title":"accessibility: enlarged text pushes cited-account results off screen in WebKit"},{"citation":"resolved","description":"Now: Every ThesisOpen note is described as still waiting for a child, even after noteOf reports children > 0. The same page then reports 1 child call and correctly removes the forfeiture action.\nChange to: In the note page, select the explanation using both status and children: for ThesisOpen with a child use A child call is attached; its settlement releases this thesis bond. Keep the existing grace-period explanation only when children is zero.\nWhy: A writer deciding whether the thesis is qualified needs a truthful one-line state; the current explanation contradicts the live child count and implies another call is required.","line":73,"path":"web/src/lib.ts","reproduction":"At 390×844, open #/note/0x1 with a valid thesis fixture: NotePosted targetBps=0, horizonDays=0, timestamp eight days ago; noteOf returns status=6 (ThesisOpen), children=1, deposit=0.25 IMD. The page renders A thesis that still waits for the author to hang a call off it within 7 days, while Child grace shows an expired date and 1 child call. This is a reachable state while the attached price call is still open. Expected an attached-child/pending-settlement explanation. Confirmed in Chromium against the production export.","severity":"medium","snippet":"  6: 'A thesis that still waits for the author to hang a call off it within 7 days.',","title":"bug: a thesis with a child still says it is waiting for a child call"},{"citation":"resolved","description":"Now: The note page explains observations in a 44-word, three-sentence paragraph, adds another long raw-reading paragraph, then explains the keeper mechanism again above its controls. Pools also opens with a multi-sentence explanation.\nChange to: Replace the observation paragraph with Settlement needs three readings, at least 20 blocks apart and spanning one hour, within six hours after the horizon. Follow with the short necessary rule More adverse readings can replace a slot. Replace the keeper introduction with Anyone may observe or settle; settling earns 10% only from a forfeited bond. Compress the pools introduction to Fees, stamps and forfeited bonds fund the weekly thesis prize (70%) and daily writer refunds (30%), all in IMD. Preserve the existing raw-unit labels and unavailable-read explanation.\nWhy: The brief says neither audience reads documentation and explicitly asks for mechanics in one line each, never in a paragraph. These retained introductions slow readers reaching evidence and controls.","line":144,"path":"web/src/pages/Note.tsx","reproduction":"At 390×844, open #/note/0x1 with a priced-call fixture and scroll to Observations. The first explanation is 44 words in three sentences, beginning Inside the 6-hour window and ending a keeper usually runs them; it is followed by the raw-reading paragraph and a further Keeper actions introduction on open calls. Expected one concise rule beside each control. Open #/pools for the same multi-sentence introductory pattern. This is visible at both 390 and 1440 px and is source-rendered copy, not note text written by an author.","severity":"medium","snippet":"            Inside the 6-hour window after the horizon anyone may record a reading of the venue. At least 3 readings spaced 20 blocks and spanning an\n            hour are needed; a more adverse reading replaces a slot. The site shows them; a keeper usually runs them.\n          </p>","title":"fidelity: lifecycle mechanics still arrive as explanatory paragraphs"},{"citation":"resolved","description":"Now: The added .eyebrow treatment puts widely tracked uppercase labels around the home headline, prize, leaderboards, author statistics and duel panels. The home kicker repeats the job of the headline, while Posting wallets and Receipts index add a second label to each ranking.\nChange to: Delete the home kicker and redundant Receipts index label; rename the author section heading to Author leaderboard and remove its Posting wallets kicker. Render necessary prize, statistic and duel labels with the existing .small style, normal case and zero tracking. Close the deleted label margins so the data moves up.\nWhy: The review bar rejects repeated kicker labels and content tracking above +0.04em. The ledger can keep its existing serif headings and monospace data without a second hierarchy of captions.","line":173,"path":"web/src/styles.css","reproduction":"At 390 and 1440 px, open #/ and inspect the heading, prize and two leaderboard headers. taste@1 reports four all-caps content labels, four labels at +0.080em tracking (bar +0.040em), and two detected kickers. Inspect .eyebrow in web/src/styles.css:173; the same rule also styles the author and duel pages. Screenshots confirm the spaced uppercase labels above or beside the useful titles.","severity":"medium","snippet":".eyebrow { display: block; font: 0.76rem/1.5 var(--font-mono); text-transform: uppercase; letter-spacing: 0.08em; color: var(--ink-soft); margin: 0 0 0.5rem; }","title":"taste: repeated tracked uppercase kickers compete with the ledger headings"}],"hash":"9c738a1073159ccafccc36bf34178a7a7f66f526d6572c4875d6705f1c1ef802","nodeId":"79c85e34-a9ad-42cf-b5bc-81405937626a","outcome":"completed","summary":"| Area | Count / result |\n|---|---|\n| Requirements | **57: 53 met, 4 missing, 0 faked** |\n| Broken features | 1 state-description bug; 1 accessibility defect |\n| First screen at 390px | **No reader scoreboard**; first record starts at y=963px |\n| taste@1 | **11/16 passed** |\n| Copy | 3 findings |\n| Share cards | **3/3 page types verified**, 1200×630, local rendering and download |\n\nSaved **6 findings: 2 high, 4 medium** to [.imd-findings.json](/home/imd2/.identitymd/work/d89b2921-8c80-4361-bd23-f03cdc8eae4f/79c85e34-a9ad-42cf-b5bc-81405937626a/.imd-findings.json). Site files are unchanged.\n\n- **High — fidelity:** Live dollar context is absent from both the prize and posting cost.\n- **High — fidelity:** The phone first screen hides the reader scoreboard.\n- **Medium — accessibility:** Enlarged text pushes cited-account results off screen in WebKit.\n- **Medium — bug:** A thesis with a child still says it needs a child call.\n- **Medium — fidelity:** Lifecycle mechanics remain explanatory paragraphs.\n- **Medium — taste:** Repeated tracked uppercase labels compete with the headings.\n\nEach finding contains an exact source citation, proposed change, and measured reproduction. Reopen the build for the two high findings.\n\nNo critical address, approval, or remote-script defect was found. Asset hashes, deployment ABI hashes, live contract bindings, deployed code, STAMP, and seal encodings verified. Chromium passed all 21 existing checks. Independent WebKit checks confirmed sharing and isolated the enlarged-text defect. Populated states used explicit fixtures; the live hook had zero notes. No transactions were broadcast.\n\nThe complete requirement checklist follows. Related phrases share a row.\n\n| # | Requirement | Status and evidence |\n|---|---|---|\n| 1 | “keep its stack” | **Met** — [package.json][package], React/Vite/viem retained |\n| 2 | “ledger look” | **Met** — [styles.css][styles], paper, ink, serif type and rules |\n| 3 | “runtime manifest” | **Met** — [config.ts][config] loads the verified export |\n| 4 | “transaction builders and review posture” | **Met** — [chain.ts][chain], [Post][post], [Seal][seal]; simulations and guarded signing |\n| 5 | Readers find “who is actually right”; writers “prove they are” | **Met** — [Home][home] comparisons, [Author][author] records and [Post][post] flow; placement fails separately below |\n| 6 | “Above the fold, in this order” | **Missing** — [Home][home]; ranking and subsequent sections fall below the phone fold |\n| 7 | Prize from “hook.thesisPool(week())” | **Met** — [Home][home], `Prize` |\n| 8 | Prize “≈$ at the live IMD price” | **Missing** — [price.tsx][price] always displays unavailable |\n| 9 | “judging-opens time” | **Met** — [Home][home], live countdown and date |\n| 10 | “count of eligible theses” | **Met** — [Home][home], `eligibleTheses(week).length` |\n| 11 | Zero-state “no thesis entered yet” | **Met** — [Home][home], invitation links to thesis posting |\n| 12 | Rank “records(address).score” from “NotePosted authors” | **Met** — [Home][home], [notes.ts][notes], [scoreboard.ts][scoreboard] |\n| 13 | “posted/right/wrong/broken/retracted and a hit rate” | **Met** — [Home][home], author ranking rows |\n| 14 | Tabs “by score” and “by hit rate” | **Met** — [Home][home]; both reorder the records |\n| 15 | Duels: parent, another author, same X, opposite side | **Met** — [scoreboard.ts][scoreboard], `findDuels` |\n| 16 | Both authors, claims, bonds and horizons “side by side” | **Met** — [Duel][duel], `DuelPair` |\n| 17 | Feed with “recent verdicts” from Settled above it | **Met** — [Home][home], `Verdicts` then [Feed][feed] |\n| 18 | “Receipts for every call: a price, a deadline, a bond, and the chain’s verdict” | **Met** — [scoreboard.ts][scoreboard], `REASONS`; rendered on Home |\n| 19 | “Prove it: a difficulty-weighted score, public duels, a weekly prize” | **Met** — same `REASONS` list |\n| 20 | “Readers pay writers… (coming with the page market)” | **Met** — same list, explicitly future functionality","treeHash":null,"usage":{"cachedInputTokens":4263680,"inputTokens":171289,"model":"gpt-6-astra","outputTokens":29776,"runtime":"codex","turns":10,"wallClockMs":907447}},{"artifacts":[],"attempt":1,"bundleHash":null,"device":"7eb063fcf0cf5626","findings":[{"citation":"resolved","description":"Merged from the site_content_check specialist finding (id 335f1f6ce325d650d0468cc6e0cd9439f5e8e2075e106102bdb421001f30667d) and reproduced against the committed dist in headless Chromium with the worker's own RPC fixture. The skip link is href=\"#main\". Activating it sets window.location.hash to '#main'; the hashchange handler focuses main and returns early, so the React route state keeps the note/duel/author page displayed while the URL is now <site>/#main. Share (web/src/share.tsx line 74) builds its text from window.location.href at render time, and the page re-renders every second from the shared clock, so the clipboard text and the 'Share text' field end in '#main'. A reader who opens that link in a fresh tab gets route 'main', which falls through to HomePage (App.tsx line 109): the scoreboard, not the record that was shared. This affects every record route (#/note, #/duel, #/author). Not a hosting blocker: no address, approval or script is involved.","line":22,"path":"web/src/App.tsx","reproduction":"State: a loaded feed containing note 1 (any status). Steps: open #/note/0x1; Tab to the 'Skip to content' link and press Enter (location.hash becomes '#main' while 'Note № 1' stays displayed); wait at least one second; press Share (clipboard mocked or real). Expected: share text and clipboard end with '#/note/0x0000…0001'. Actual (reproduced, Chromium, committed dist): share text and clipboard are 'Marginalia note № 1 by 0x1111…1111: Up +50%. right.\\nhttp://127.0.0.1:<port>/preview/#main'. Opening that copied URL in a fresh tab renders h1 'Say it with a bond.' with the prize scoreboard, not Note № 1. Fix direction: after focusing main, restore the previous route hash (history.replaceState) or build the share URL from the route state rather than window.location.href.","severity":"low","snippet":"      if (window.location.hash === '#main') { document.getElementById('main')?.focus(); return; }","title":"Skip-to-content link replaces the route hash, so Share copies a scoreboard link instead of the note, duel or author page"},{"citation":"resolved","description":"The note and duel routes accept a short hex id (#/note/0x2 and #/duel/0x2 both normalise through noteIdFromIndex), and the brief documents #/post?counter=<hex-id>. The counter prefill, however, compares the raw query value against the full 32-byte note id string, so any id that is not the exact 66-character zero-padded form never matches. With a loaded feed the notice stays on 'Reading the target call…' indefinitely, no field is prefilled, and because counterOk (line 173) requires a resolved counter, the Post button stays disabled and sendNote throws. The site's own 'Counter this call' button passes the full id, so the UI path works; the documented and agent-facing URL form with a short id does not.","line":32,"path":"web/src/pages/Post.tsx","reproduction":"State: a loaded feed containing note 2 (an open call by another wallet). Input: open #/post?counter=0x2. Expected: 'Counter to note № 2: token, opposite side and parent are prefilled.' and a postable form (as #/post?counter=0x0000000000000000000000000000000000000000000000000000000000000002 produces; reproduced in Chromium: the full-id form prefills and selects the opposite side). Actual (reproduced): the notice reads 'Reading the target call…' after the feed has loaded, nothing is prefilled, and the post control remains disabled. Fix direction: normalise counterId with the same rule as the note route (rawId.length === 66 ? lowercase : noteIdFromIndex(BigInt(rawId))).","severity":"low","snippet":"  const counter = feed?.notes.find(n => n.id.toLowerCase() === counterId?.toLowerCase());","title":"#/post?counter=<short hex id> never resolves the target call and leaves posting disabled"},{"citation":"resolved","description":"The seal page is the one place where the spender is the hook, not the router (web/src/pages/Seal.tsx line 45). explainError maps every allowance revert to copy naming the router. If hook.seal's transferFrom of the 0.1 IMD stamp fails on allowance (for example the hook allowance was consumed by a seal sent from another tab or wallet session between the 15-second allowance poll and the click), the writer is told to approve the router, which the posting page would do for a different amount and a different spender; it cannot fix the seal. The exact-amount hook approval rule itself is implemented correctly (verified: the only hook approval in the source is Seal.tsx line 45 for exactly limits.stamp, gated on STAMP === 0.1 IMD; all other approvals target hook.router()).","line":255,"path":"web/src/lib.ts","reproduction":"State: wallet with hook allowance shown as 0.1 IMD so 'Seal the call' is offered; mature checkpoint; the seal simulation reverts with ERC20InsufficientAllowance(hook, 0, 100000000000000000). Steps: press 'Seal the call'. Expected: an error naming the hook stamp allowance (for example 'The hook’s 0.1 IMD stamp allowance is too small. Approve the stamp first.'). Actual (reproduced in Chromium with the simulation forced to revert with that error): the alert reads 'The router’s IMD allowance is too small. Approve first.' Fix direction: give explainError the spender context or let Seal.tsx map allowance errors to hook-specific copy.","severity":"low","snippet":"    [/ERC20InsufficientAllowance|allowance/i, 'The router’s IMD allowance is too small. Approve first.'],","title":"On the seal page an IMD allowance revert is explained as a router allowance problem, pointing the writer at the wrong approval"},{"citation":"resolved","description":"The brief requires the prize line and the first-note cost to show an approximate dollar amount at the live IMD price. deployment.json and network.json carry no USD oracle, price feed address or price endpoint, the reference forbids hard-coding addresses outside deployment.json, and the site has no backend. The worker chose to print 'USD context unavailable' and documented it (VALIDATION.md, AGENTS.md, llms.txt). This is a reasonable resolution of a requirement the supplied inputs cannot satisfy, but it is a stated deviation from the brief that the requester should accept explicitly or supply a verified source for. Non-blocking.","line":3,"path":"web/src/price.tsx","reproduction":"Open #/ with any loaded prize: the prize block prints '<N> IMD' followed by 'USD context unavailable'; open #/post as a new wallet: the posting cost prints '0.6 IMD' plus 'USD context unavailable'. The brief's expected output is '… IMD (≈$X at the live IMD price)'. No input to the site produces a dollar figure.","severity":"info","snippet":"  return <span className=\"small\" title=\"The deployment supplies no verified IMD/USD price source.\">USD context unavailable</span>;","title":"Brief items 1(a) and 6 ask for a dollar figure at the live IMD price; the site shows 'USD context unavailable' instead (scope question, not a defect)"},{"citation":"resolved","description":"The Open Graph and Twitter card tags point at './share-card.png'. X's card crawler and the OG specification expect absolute URLs; a relative image reference is typically dropped, so a bio link to the IPFS gateway path will unfurl with title and description but most likely without the ledger image. The brief defers the per-page unfurl image to a later service, and the client-side Share card (which attaches the PNG directly) is the primary sharing path, so this does not block v2. It is recorded because readers 'arrive from a shared card or a link in a bio' and the base card is the only image available for that path. The absolute gateway URL is not knowable at build time; the OG service can supply it later.","line":12,"path":"web/index.html","reproduction":"Fetch <gateway>/ipfs/<cid>/ as X's crawler would and read the meta tags: twitter:image is './share-card.png' (relative). Expected for a reliable unfurl: an absolute https URL. Observed in the committed dist/index.html: relative paths for both og:image and twitter:image.","severity":"info","snippet":"    <meta property=\"og:image\" content=\"./share-card.png\" />","title":"Relative og:image / twitter:image URLs are unlikely to unfurl on X until the separate OG service exists (carried over from v1, non-blocking)"}],"hash":"add2ffd0295ed17024cd07cef0d794ba6f0cf718683ad0a07491d73029d1c502","nodeId":"4b6879d3-e2cb-40bc-a390-91f9f43feb62","outcome":"completed","summary":"The audit is complete. Findings are written to `.imd-findings.json` at the repository root. No file in the tree was changed.\n\n**Verdict: no critical or high finding.** Nothing refuses hosting. The three address, approval and script checks in the review brief hold:\n\n- **Hook approval** exists only on the seal page, for exactly the live STAMP (gated on it equalling 0.1 IMD), to the hook address read from the runtime manifest. Every other approval targets hook.router() for exactly the posting total.\n- **Share card** is drawn on a canvas with system fonts and makes no network request. The worker's own 21-scenario browser suite, which I re-ran here in headless Chromium, confirms zero outbound calls during sharing.\n- **Receipts parser** returns citation metadata only. Note.author always comes from the NotePosted event. Spoofed hosts, userinfo tricks and mid-text URLs are rejected.\n\n**What I verified beyond the specialist's report.** The committed dist is byte-identical to a fresh build from source. The four exported ABI files match the forge-compiled ABIs exactly, and the hand-written TypeScript ABI subsets match those files entry for entry. The manifest check passes against the pinned deployment and network files. The agent documents in dist are verbatim copies. Event signatures, struct layouts, the week and judging-open arithmetic, the seal and reveal windows, the observe and settle gates, and the sealHash encoding all match the contract source.\n\n**Findings, ranked by severity.** All three defects were reproduced in a browser against the committed bundle.\n\n1. **Low.** The skip-to-content link sets the hash to `#main` while keeping the record page displayed, so Share copies a link that opens the scoreboard in a fresh tab. This is the specialist's finding, confirmed and kept.\n2. **Low.** The documented `#/post?counter=<hex-id>` form only works with the full 66-character id. A short id such as `0x2` leaves the page on \"Reading the target call…\" with posting disabled, even though the note and duel routes accept short ids.\n3. **Low.** On the seal page an allowance revert is explained as \"The router's IMD allowance is too small\", which points the writer at the wrong spender.\n4. **Info.** The brief asks for dollar amounts at the live IMD price. The site prints \"USD context unavailable\" because the supplied inputs carry no price source. This is a documented scope deviation for the requester to accept or resolve.\n5. **Info.** The Open Graph image tags are relative URLs, which X's crawler is unlikely to honour until the separate unfurl service exists. Carried over from v1, non-blocking.\n\nNo Foundry proof files were needed since no finding is in Solidity code or above medium severity.","treeHash":null,"usage":{"cachedInputTokens":4073886,"inputTokens":706,"model":"claude-fable-5-1","outputTokens":45348,"runtime":"claude","turns":58,"wallClockMs":1307510}}],"verification":[{"checks":[],"detail":"paths and tree verified; no suite was run for this kind of work","evaluation":"structural","profile":"none","status":"accepted","submissionHash":"9bafe6b70c7f9ca1f1c8cd3dd93a258c10bb337a6ba5d977f618df759d3789e4","verifiedTreeHash":"8e0cb9e5b56c34b6830863349c073a0afbe16762","verifierVersion":"0.1.0+fb8278c5"}]}