| const state = { |
| registry: null, |
| selectedId: null, |
| filter: "all", |
| query: "", |
| tab: "article", |
| contextMode: "canonical" |
| }; |
|
|
| const escapeHtml = (value) => String(value ?? "") |
| .replaceAll("&", "&") |
| .replaceAll("<", "<") |
| .replaceAll(">", ">") |
| .replaceAll('"', """) |
| .replaceAll("'", "'"); |
|
|
| const titleCase = (value) => String(value).replaceAll("_", " ").replace(/\b\w/g, (letter) => letter.toUpperCase()); |
|
|
| function compactNumber(value) { |
| if (typeof value !== "number") return String(value ?? ""); |
| if (value !== 0 && Math.abs(value) < 0.001) return value.toExponential(3); |
| return String(Number(value.toPrecision(8))); |
| } |
|
|
| function assertionDetail(assertion) { |
| const detail = assertion.detail; |
| if (typeof detail === "string") return detail; |
| if (detail && typeof detail === "object") { |
| if (typeof detail.absolute === "number") return `absolute difference = ${compactNumber(detail.absolute)}`; |
| if (typeof detail.observed === "number") { |
| const bound = detail.required ?? detail.required_maximum; |
| return `${compactNumber(detail.observed)} against bound ${compactNumber(bound)}`; |
| } |
| if (assertion.outcome === "satisfied") return "All declared diagnostics passed"; |
| } |
| return JSON.stringify(detail ?? null); |
| } |
|
|
| function evidenceResults(evidence) { |
| const artifact = evidence.artifact_result || {}; |
| if (evidence.verifier.name === "kwant_transport") { |
| return [ |
| { label: "T(+tau)", value: compactNumber(artifact.runs?.plus_tau?.transmission), tone: "normal" }, |
| { label: "T(-tau)", value: compactNumber(artifact.runs?.minus_tau?.transmission), tone: "normal" }, |
| { label: "absolute delta", value: compactNumber(artifact.claims?.transmission_even_in_tau?.absolute), tone: "normal" } |
| ]; |
| } |
| if (evidence.verifier.name === "fmm_occupancy") { |
| return [ |
| { label: "constant mean", value: compactNumber(artifact.regimes?.constant_mean?.union_bound_bad_probability), tone: "normal" }, |
| { label: "proportional mean", value: compactNumber(artifact.regimes?.proportional_mean?.union_bound_bad_probability), tone: "normal" }, |
| { label: "inconsistent hybrid", value: compactNumber(artifact.regimes?.inconsistent_hybrid?.union_bound_bad_probability), tone: "danger" } |
| ]; |
| } |
| const proof = Object.values(artifact.claims || {})[0] || {}; |
| return [ |
| { label: "proof", value: proof.proof || "formal proof", tone: "normal" }, |
| { label: "source", value: proof.source || "bundled source", tone: "normal" }, |
| { label: "premises", value: "remain explicit", tone: "danger" } |
| ]; |
| } |
|
|
| function rawVerifierContext(record) { |
| return record.evidence.flatMap((evidence) => [ |
| `${evidence.verifier.name} (${evidence.id})`, |
| ...evidence.assertions.map((item) => `${item.assertion}: outcome=${item.outcome}; detail=${assertionDetail(item)}`) |
| ]).join("\n"); |
| } |
|
|
| function canonicalContext(record) { |
| const dimensions = Object.entries(record.status.dimensions) |
| .map(([name, value]) => `${titleCase(name)}: ${value}`); |
| const obligations = record.obligations.map((item) => `Open: ${item.message}`); |
| return [`Status: ${record.status.derived}`, ...dimensions, ...obligations].join("\n"); |
| } |
|
|
| function normalizeCanonicalClaim(record, registry) { |
| const claim = record.claim.record; |
| const study = registry.studies.find((item) => item.id === record.study?.id); |
| const evidence = record.evidence.map((item) => ({ |
| id: item.id, |
| verifier: item.verifier.name, |
| classification: item.classification, |
| authenticated: item.authenticated, |
| assertions: item.assertions.map((assertion) => ({ |
| id: assertion.assertion, |
| outcome: assertion.outcome, |
| detail: assertionDetail(assertion) |
| })), |
| results: evidenceResults(item), |
| formalization: item.record?.formalization || null |
| })); |
| const isFormal = record.evidence.some((item) => item.verifier.evidence_kind === "formal_proof"); |
| const evidenceType = !record.evidence.length ? "open" : (isFormal ? "formal" : "computational"); |
| return { |
| id: record.claim.id, |
| study_id: record.study?.id, |
| study: study?.name || record.study?.record?.name || "Unassigned study", |
| domain: claim.topic, |
| role: record.claim.role || "background", |
| kind: claim.kind, |
| evidence_type: evidenceType, |
| title: record.claim.title, |
| question: record.narrative.claim_question, |
| plain_language_conclusion: record.narrative.plain_language_conclusion, |
| scope_summary: record.narrative.scope_summary, |
| remaining_uncertainty: record.narrative.remaining_uncertainty, |
| statement: record.claim.statement_text, |
| latex: typeof claim.statement === "object" ? claim.statement.latex : "", |
| status: record.status.derived, |
| scope: record.scope_boundary.scope, |
| dimensions: record.status.dimensions, |
| conditions: record.scope_boundary.conditions, |
| limitations: record.scope_boundary.limitations, |
| obligations: record.obligations.map((item) => item.message), |
| dependencies: record.dependency_closure.nodes, |
| evidence, |
| formalizations: record.formalizations || [], |
| raw_context: rawVerifierContext(record), |
| canonical_context: canonicalContext(record), |
| canonical_state: record |
| }; |
| } |
|
|
| function normalizeRegistry(registry) { |
| if (registry.claims[0]?.claim?.record) { |
| const claims = registry.claims.map((record) => normalizeCanonicalClaim(record, registry)); |
| claims.sort((left, right) => { |
| const featured = (claim) => claim.study_id === "twisted-ribbon" ? 0 : 1; |
| return featured(left) - featured(right) || left.id.localeCompare(right.id); |
| }); |
| return { ...registry, claims }; |
| } |
| return registry; |
| } |
|
|
| function selectedClaim() { |
| return state.registry.claims.find((claim) => claim.id === state.selectedId) || state.registry.claims[0]; |
| } |
|
|
| function studyForClaim(claim) { |
| return state.registry.studies.find((study) => study.id === claim.study_id); |
| } |
|
|
| function filteredClaims() { |
| const needle = state.query.trim().toLowerCase(); |
| return state.registry.claims.filter((claim) => { |
| const matchesType = state.filter === "all" || claim.evidence_type === state.filter; |
| const haystack = [claim.id, claim.study, claim.domain, claim.title, ...claim.evidence.map((item) => item.verifier)].join(" ").toLowerCase(); |
| return matchesType && (!needle || haystack.includes(needle)); |
| }); |
| } |
|
|
| function renderMetrics() { |
| const claims = state.registry.claims; |
| document.querySelector("#study-count").textContent = new Set(claims.map((claim) => claim.study)).size; |
| document.querySelector("#claim-count").textContent = claims.length; |
| const indexed = state.registry.studies.reduce((sum, study) => sum + study.coverage.total, 0); |
| document.querySelector("#claim-coverage-label").textContent = `Full states of ${indexed} indexed`; |
| document.querySelector("#coverage-note").textContent = `${claims.length} of ${indexed} shown`; |
| document.querySelector("#evidence-count").textContent = claims.reduce((sum, claim) => sum + claim.evidence.length, 0); |
| document.querySelector("#obligation-count").textContent = claims.reduce((sum, claim) => sum + claim.obligations.length, 0); |
| } |
|
|
| function renderCatalog() { |
| const claims = filteredClaims(); |
| const list = document.querySelector("#claim-list"); |
| document.querySelector("#visible-count").textContent = `${claims.length} claim${claims.length === 1 ? "" : "s"}`; |
| if (!claims.length) { |
| list.innerHTML = '<div class="empty-state">No claims match this view.</div>'; |
| return; |
| } |
| list.innerHTML = claims.map((claim) => ` |
| <button class="claim-row ${claim.id === state.selectedId ? "selected" : ""}" data-claim-id="${escapeHtml(claim.id)}" type="button" role="listitem"> |
| <div class="claim-row-top"> |
| <span class="claim-id">${escapeHtml(claim.id)}</span> |
| <span class="role-badge role-${escapeHtml(claim.role)}">${escapeHtml(titleCase(claim.role))}</span> |
| </div> |
| <h3>${escapeHtml(claim.title)}</h3> |
| <div class="claim-row-meta"> |
| <span>${escapeHtml(claim.study)}</span> |
| <span>${escapeHtml(claim.evidence[0]?.verifier || "no verifier")}</span> |
| </div> |
| </button> |
| `).join(""); |
| list.querySelectorAll("[data-claim-id]").forEach((button) => { |
| button.addEventListener("click", () => { |
| state.selectedId = button.dataset.claimId; |
| state.tab = "article"; |
| renderCatalog(); |
| renderDetail(); |
| if (window.innerWidth < 761) document.querySelector("#detail-panel").scrollIntoView({ behavior: "smooth", block: "start" }); |
| }); |
| }); |
| } |
|
|
| function detailHeader(claim) { |
| const study = studyForClaim(claim); |
| return ` |
| <header class="detail-header"> |
| <p class="eyebrow">${escapeHtml(claim.domain)} / ${escapeHtml(claim.id)}</p> |
| <div class="detail-title-row"> |
| <h2>${escapeHtml(claim.title)}</h2> |
| <span class="status-badge status-${escapeHtml(claim.status)}">${escapeHtml(claim.status)}</span> |
| </div> |
| <p class="detail-statement">${escapeHtml(claim.statement)}</p> |
| <section class="narrative-lead" aria-label="Plain-language claim summary"> |
| <div><small>Question</small><p>${escapeHtml(claim.question)}</p></div> |
| <div><small>Resolution</small><p>${escapeHtml(claim.plain_language_conclusion)}</p></div> |
| </section> |
| <p class="coverage-disclosure"> |
| ${study.coverage.is_complete ? "Complete registry" : "Curated registry"}: |
| ${study.coverage.shown} of ${study.coverage.total} claims have full records here. |
| The study graph indexes all ${study.coverage.total}. |
| </p> |
| <div class="scope-line"> |
| <span class="type-badge">${escapeHtml(claim.kind)}</span> |
| <span class="type-badge">scope: ${escapeHtml(claim.scope)}</span> |
| <span class="type-badge">revision: ${escapeHtml(claim.dimensions.revision)}</span> |
| </div> |
| </header> |
| <nav class="tabs" aria-label="Claim views"> |
| ${[ |
| ["article", "Article"], |
| ["study", "Study overview"], |
| ["overview", "Overview"], |
| ["formalization", "Formalization"], |
| ["evidence", "Evidence"], |
| ["graph", "Claim graph"], |
| ["context", "LLM context"], |
| ["json", "State JSON"] |
| ].map(([id, label]) => `<button class="tab ${state.tab === id ? "active" : ""}" data-tab="${id}" type="button">${label}</button>`).join("")} |
| </nav> |
| `; |
| } |
|
|
| function equationById(study, equationId) { |
| return study.equations.find((equation) => equation.id === equationId); |
| } |
|
|
| function articleClaimBlock(study, claimId, selectedId) { |
| const index = study.claim_index.find((item) => item.id === claimId); |
| const record = state.registry.claims.find((item) => item.id === claimId); |
| if (!index) return ""; |
| return ` |
| <button class="article-claim ${claimId === selectedId ? "current" : ""}" data-article-claim="${escapeHtml(claimId)}" type="button"> |
| <span class="claim-id">${escapeHtml(claimId)}</span> |
| <span class="article-claim-copy"> |
| <strong>${escapeHtml(index.title)}</strong> |
| <small>${escapeHtml(record?.plain_language_conclusion || "Indexed claim")}</small> |
| </span> |
| <span class="status-badge status-${escapeHtml(index.status)}">${escapeHtml(index.status)}</span> |
| </button> |
| `; |
| } |
|
|
| function articleEquationBlock(study, equationId) { |
| const equation = equationById(study, equationId); |
| if (!equation) return ""; |
| const number = study.equations.findIndex((item) => item.id === equationId) + 1; |
| const links = equation.correspondence || []; |
| return ` |
| <figure class="equation-block"> |
| <figcaption> |
| <span>Equation ${number}</span> |
| <span class="equation-id">${escapeHtml(equation.id)}</span> |
| <span class="equation-role equation-role-${escapeHtml(equation.role)}">${escapeHtml(titleCase(equation.role))}</span> |
| </figcaption> |
| <div class="math-display">\\[${escapeHtml(equation.latex)}\\]</div> |
| <p>${escapeHtml(equation.plain_language)}</p> |
| <div class="equation-links"> |
| ${equation.depends_on.length ? `<span>Uses ${equation.depends_on.map(escapeHtml).join(", ")}</span>` : "<span>Starting definition</span>"} |
| ${links.map((item) => `<span class="correspondence correspondence-${escapeHtml(item.status)}">${escapeHtml(titleCase(item.type))}: ${escapeHtml(item.status)}</span>`).join("")} |
| </div> |
| </figure> |
| `; |
| } |
|
|
| function articleView(claim) { |
| const study = studyForClaim(claim); |
| const article = study.article; |
| if (!article) return '<div class="empty-state">This study has no ordered article yet.</div>'; |
| return ` |
| <section class="article-view"> |
| <header class="article-heading"> |
| <p class="eyebrow">Versioned scientific argument</p> |
| <h3>${escapeHtml(article.title)}</h3> |
| <p>${escapeHtml(study.research_question)}</p> |
| </header> |
| <nav class="article-outline" aria-label="Article sections"> |
| ${article.sections.map((section, index) => `<a href="#section-${escapeHtml(section.id)}"><span>${index + 1}</span>${escapeHtml(section.title)}</a>`).join("")} |
| </nav> |
| <div class="article-body"> |
| ${article.sections.map((section, index) => ` |
| <section class="article-section" id="section-${escapeHtml(section.id)}"> |
| <div class="section-number">${index + 1}</div> |
| <div class="section-content"> |
| <h3>${escapeHtml(section.title)}</h3> |
| ${section.blocks.map((block) => { |
| if (block.type === "prose") return `<p class="article-prose">${escapeHtml(block.text)}</p>`; |
| if (block.type === "equation") return articleEquationBlock(study, block.ref); |
| return articleClaimBlock(study, block.ref, claim.id); |
| }).join("")} |
| </div> |
| </section> |
| `).join("")} |
| </div> |
| <footer class="article-resolution"> |
| <small>Current study resolution</small> |
| <p>${escapeHtml(study.resolution_summary)}</p> |
| </footer> |
| </section> |
| `; |
| } |
|
|
| function studyView(claim) { |
| const study = studyForClaim(claim); |
| return ` |
| <section> |
| <div class="study-heading"> |
| <div> |
| <p class="eyebrow">Research question</p> |
| <h3>${escapeHtml(study.research_question)}</h3> |
| </div> |
| <span class="coverage-badge">${study.coverage.shown} / ${study.coverage.total} full records</span> |
| </div> |
| <div class="study-summary-grid"> |
| <section><h3 class="section-title">Approach</h3><p>${escapeHtml(study.approach_summary)}</p></section> |
| <section><h3 class="section-title">Current resolution</h3><p>${escapeHtml(study.resolution_summary)}</p></section> |
| </div> |
| <h3 class="section-title chain-title">Complete claim chain</h3> |
| <ol class="study-chain"> |
| ${study.claim_index.map((item) => ` |
| <li class="${item.id === claim.id ? "current" : ""} ${item.shown ? "shown" : "indexed"}"> |
| <div> |
| <span class="claim-id">${escapeHtml(item.id)}</span> |
| <strong>${escapeHtml(item.title)}</strong> |
| </div> |
| <div class="chain-meta"> |
| <span class="role-badge role-${escapeHtml(item.role)}">${escapeHtml(titleCase(item.role))}</span> |
| <span>${item.shown ? "Full record" : "Indexed only"}</span> |
| </div> |
| </li> |
| `).join("")} |
| </ol> |
| <p class="graph-note">Indexed-only nodes remain visible so a curated demo cannot be mistaken for the complete scientific argument.</p> |
| </section> |
| `; |
| } |
|
|
| function overviewView(claim) { |
| return ` |
| <section> |
| <h3 class="section-title">Epistemic dimensions</h3> |
| <div class="dimension-grid"> |
| ${Object.entries(claim.dimensions).map(([name, value]) => ` |
| <div class="dimension"><small>${escapeHtml(name)}</small><strong>${escapeHtml(value)}</strong></div> |
| `).join("")} |
| </div> |
| <div class="two-column"> |
| <section> |
| <h3 class="section-title">Scope in plain language</h3> |
| <p>${escapeHtml(claim.scope_summary)}</p> |
| </section> |
| <section> |
| <h3 class="section-title">Remaining uncertainty</h3> |
| <p>${escapeHtml(claim.remaining_uncertainty)}</p> |
| </section> |
| </div> |
| <div class="two-column"> |
| <section> |
| <h3 class="section-title">Open obligations</h3> |
| <ul class="plain-list">${claim.obligations.map((item) => `<li>${escapeHtml(item)}</li>`).join("")}</ul> |
| </section> |
| <section> |
| <h3 class="section-title">Does not establish</h3> |
| <ul class="plain-list limitations">${claim.limitations.map((item) => `<li>${escapeHtml(item)}</li>`).join("")}</ul> |
| </section> |
| </div> |
| <div class="two-column"> |
| <section> |
| <h3 class="section-title">Declared conditions</h3> |
| <ul class="plain-list">${claim.conditions.map((item) => `<li>${escapeHtml(item)}</li>`).join("")}</ul> |
| </section> |
| <section> |
| <h3 class="section-title">Formal statement</h3> |
| <div class="context-panel"><pre>${escapeHtml(claim.latex)}</pre></div> |
| </section> |
| </div> |
| </section> |
| `; |
| } |
|
|
| function evidenceView(claim) { |
| if (!claim.evidence.length) { |
| return '<div class="empty-state compact">No verifier evidence targets this claim revision. Its status remains declared, proposed, or conditional from dependencies.</div>'; |
| } |
| return claim.evidence.map((record) => ` |
| <section class="evidence-record"> |
| <div class="evidence-heading"> |
| <div><p class="eyebrow">${escapeHtml(record.id)}</p><h3>${escapeHtml(record.verifier)}</h3></div> |
| <span class="status-badge status-${claim.status}">${escapeHtml(record.classification)}</span> |
| </div> |
| <table class="assertion-table"> |
| <thead><tr><th>Assertion contract</th><th>Outcome</th><th>Observed detail</th></tr></thead> |
| <tbody> |
| ${record.assertions.map((item) => `<tr><td><code>${escapeHtml(item.id)}</code></td><td class="outcome-${escapeHtml(item.outcome)}">${escapeHtml(item.outcome)}</td><td>${escapeHtml(item.detail)}</td></tr>`).join("")} |
| </tbody> |
| </table> |
| <div class="result-strip"> |
| ${record.results.map((item) => `<div class="result-item ${item.tone === "danger" ? "danger" : ""}"><small>${escapeHtml(item.label)}</small><strong>${escapeHtml(item.value)}</strong></div>`).join("")} |
| </div> |
| <p class="context-warning">Integrity-valid record. Cryptographic authentication: ${record.authenticated ? "present" : "not present"}.</p> |
| </section> |
| `).join(""); |
| } |
|
|
| function formalizationView(claim) { |
| if (!claim.formalizations.length) { |
| return '<div class="empty-state compact">No committed formalization request targets this claim revision.</div>'; |
| } |
| return claim.formalizations.map((record) => { |
| const statement = record.formal_statement; |
| const approval = record.semantic_approval; |
| const grounding = record.scientific_grounding; |
| return ` |
| <section class="formalization-record"> |
| <header class="formalization-heading"> |
| <div> |
| <p class="eyebrow">${escapeHtml(record.id)} / Lean 4</p> |
| <h3>${escapeHtml(statement.theorem_name)}</h3> |
| </div> |
| <div class="formalization-badges"> |
| <span class="type-badge">${escapeHtml(record.status)}</span> |
| <span class="grounding grounding-${escapeHtml(grounding.status)}">grounding: ${escapeHtml(grounding.status)}</span> |
| </div> |
| </header> |
| <p class="formalization-summary">${escapeHtml(record.summary)}</p> |
| <div class="formal-statement"><pre>${escapeHtml(statement.declaration)}</pre></div> |
| <section> |
| <h3 class="section-title">Semantic mapping</h3> |
| <table class="mapping-table"> |
| <thead><tr><th>Scientific source</th><th>Lean target</th><th>Mapping</th></tr></thead> |
| <tbody> |
| ${record.semantic_mapping.map((item) => ` |
| <tr> |
| <td>${escapeHtml(item.source)}</td> |
| <td><code>${escapeHtml(item.target)}</code></td> |
| <td><span class="mapping-status mapping-${escapeHtml(item.status)}">${escapeHtml(item.status)}</span></td> |
| </tr> |
| `).join("")} |
| </tbody> |
| </table> |
| </section> |
| <div class="two-column formalization-columns"> |
| <section> |
| <h3 class="section-title">Lean assumptions</h3> |
| <ul class="plain-list">${record.assumptions.map((item) => `<li>${escapeHtml(item)}</li>`).join("") || "<li>None recorded</li>"}</ul> |
| </section> |
| <section> |
| <h3 class="section-title">Outside the proof</h3> |
| <ul class="plain-list limitations">${record.unformalized.map((item) => `<li>${escapeHtml(item)}</li>`).join("") || "<li>None recorded</li>"}</ul> |
| </section> |
| </div> |
| <div class="grounding-note"> |
| <small>Scientific grounding</small> |
| <p>${escapeHtml(grounding.rationale)}</p> |
| </div> |
| <footer class="formalization-footer"> |
| <span>Semantic approval: <strong>${escapeHtml(approval.status)}</strong>${approval.approved_by ? ` by ${escapeHtml(approval.approved_by)}` : ""}</span> |
| <span>Statement digest: <code>${escapeHtml(statement.sha256.slice(0, 12))}</code></span> |
| </footer> |
| </section> |
| `; |
| }).join(""); |
| } |
|
|
| function graphView(claim) { |
| const nodes = [...claim.dependencies, { id: claim.id, kind: claim.kind, status: claim.status, title: claim.title, root: true }]; |
| return ` |
| <section> |
| <h3 class="section-title">Transitive dependency path</h3> |
| <div class="graph" aria-label="Claim dependency graph"> |
| ${nodes.map((node, index) => ` |
| ${index ? '<div class="graph-arrow" aria-hidden="true">→</div>' : ""} |
| <div class="graph-node ${node.kind === "assumption" ? "assumption" : ""} ${node.root ? "root" : ""}"> |
| <small>${escapeHtml(node.id)} / ${escapeHtml(node.kind)}</small> |
| <strong>${escapeHtml(node.title)}</strong> |
| <span class="status-badge status-${escapeHtml(node.status)}">${escapeHtml(node.status)}</span> |
| </div> |
| `).join("")} |
| </div> |
| <p class="graph-note">A successful child does not erase unresolved premises. Conditional status propagates through the graph, and changing a locked dependency makes descendants stale.</p> |
| </section> |
| `; |
| } |
|
|
| function contextView(claim) { |
| const study = studyForClaim(claim); |
| const mathematicalSequence = study.equations.map((equation, index) => [ |
| `Equation ${index + 1} (${equation.id}, ${equation.role}): ${equation.latex.trim()}`, |
| `Meaning: ${equation.plain_language.trim()}`, |
| `Linked claims: ${equation.claim_ids.join(", ") || "none"}`, |
| `Correspondence: ${(equation.correspondence || []).map((item) => `${item.type}=${item.status}`).join(", ") || "none"}` |
| ].join("\n")).join("\n\n"); |
| const formalizationSequence = claim.formalizations.map((item) => [ |
| `Formalization ${item.id}: status=${item.status}`, |
| `Lean theorem: ${item.formal_statement.declaration.trim()}`, |
| `Scientific grounding: ${item.scientific_grounding.status}`, |
| `Grounding rationale: ${item.scientific_grounding.rationale}`, |
| `Unformalized: ${item.unformalized.join("; ") || "none"}` |
| ].join("\n")).join("\n\n"); |
| const narrative = [ |
| `Study question: ${study.research_question}`, |
| `Study resolution: ${study.resolution_summary}`, |
| `Claim question: ${claim.question}`, |
| `Resolution: ${claim.plain_language_conclusion}`, |
| `Scope: ${claim.scope_summary}`, |
| `Still open: ${claim.remaining_uncertainty}`, |
| "", |
| "Ordered mathematical sequence:", |
| mathematicalSequence, |
| "", |
| "Formalization state:", |
| formalizationSequence || "No formalization requested", |
| "", |
| claim.canonical_context |
| ].join("\n"); |
| const content = state.contextMode === "canonical" ? narrative : claim.raw_context; |
| const warning = state.contextMode === "raw" |
| ? "Raw outputs make passes=true easy to overinterpret. The reader must reconstruct scope and dependencies." |
| : "Canonical state separates proof, corroboration, assumptions, scope, and provenance before an LLM interprets the claim."; |
| return ` |
| <section> |
| <h3 class="section-title">Same scientific record, different context</h3> |
| <div class="context-toggle"> |
| <button class="${state.contextMode === "raw" ? "active" : ""}" data-context="raw" type="button">Raw verifier output</button> |
| <button class="${state.contextMode === "canonical" ? "active" : ""}" data-context="canonical" type="button">GitScience state</button> |
| </div> |
| <div class="context-panel"><pre>${escapeHtml(content)}</pre></div> |
| <p class="context-warning">${escapeHtml(warning)}</p> |
| <div class="two-column"> |
| <section><h3 class="section-title">Evaluation question</h3><p>What is established, what remains assumed, and which conclusion would be an overgeneralization?</p></section> |
| <section><h3 class="section-title">Proposed benchmark</h3><p>Compare small and large models on fact recall, false generalization, requested controls, token use, and confidence.</p></section> |
| </div> |
| </section> |
| `; |
| } |
|
|
| function jsonView(claim) { |
| const value = { study: studyForClaim(claim), claim_state: claim.canonical_state || claim }; |
| return ` |
| <section> |
| <div class="json-toolbar"><button class="button secondary" id="copy-json" type="button">Copy JSON</button></div> |
| <div class="json-panel"><pre>${escapeHtml(JSON.stringify(value, null, 2))}</pre></div> |
| </section> |
| `; |
| } |
|
|
| function renderDetail() { |
| const claim = selectedClaim(); |
| state.selectedId = claim.id; |
| const views = { article: articleView, study: studyView, overview: overviewView, formalization: formalizationView, evidence: evidenceView, graph: graphView, context: contextView, json: jsonView }; |
| const panel = document.querySelector("#detail-panel"); |
| panel.innerHTML = `${detailHeader(claim)}<div class="tab-content">${views[state.tab](claim)}</div>`; |
| panel.querySelectorAll("[data-tab]").forEach((button) => button.addEventListener("click", () => { |
| state.tab = button.dataset.tab; |
| renderDetail(); |
| })); |
| panel.querySelectorAll("[data-context]").forEach((button) => button.addEventListener("click", () => { |
| state.contextMode = button.dataset.context; |
| renderDetail(); |
| })); |
| panel.querySelectorAll("[data-article-claim]").forEach((button) => button.addEventListener("click", () => { |
| state.selectedId = button.dataset.articleClaim; |
| renderCatalog(); |
| renderDetail(); |
| })); |
| const copyButton = panel.querySelector("#copy-json"); |
| if (copyButton) copyButton.addEventListener("click", async () => { |
| const value = { study: studyForClaim(claim), claim_state: claim.canonical_state || claim }; |
| await navigator.clipboard.writeText(JSON.stringify(value, null, 2)); |
| copyButton.textContent = "Copied"; |
| window.setTimeout(() => { copyButton.textContent = "Copy JSON"; }, 1200); |
| }); |
| if (window.MathJax?.typesetPromise) { |
| window.MathJax.typesetClear?.([panel]); |
| window.MathJax.typesetPromise([panel]).catch(() => {}); |
| } |
| } |
|
|
| function bindControls() { |
| document.querySelector("#search-input").addEventListener("input", (event) => { |
| state.query = event.target.value; |
| renderCatalog(); |
| }); |
| document.querySelectorAll("[data-filter]").forEach((button) => button.addEventListener("click", () => { |
| state.filter = button.dataset.filter; |
| document.querySelectorAll("[data-filter]").forEach((item) => item.classList.toggle("active", item === button)); |
| renderCatalog(); |
| })); |
| const dialog = document.querySelector("#submit-dialog"); |
| document.querySelector("#submit-button").addEventListener("click", () => dialog.showModal()); |
| } |
|
|
| async function init() { |
| bindControls(); |
| try { |
| const response = await fetch("claims.json?v=2"); |
| if (!response.ok) throw new Error(`Registry request failed: ${response.status}`); |
| state.registry = normalizeRegistry(await response.json()); |
| state.selectedId = state.registry.claims.find((claim) => claim.id === "GS-QT-0005")?.id || state.registry.claims[0].id; |
| renderMetrics(); |
| renderCatalog(); |
| renderDetail(); |
| } catch (error) { |
| document.querySelector("#detail-panel").innerHTML = `<div class="empty-state">Could not load the demo registry.<br>${escapeHtml(error.message)}</div>`; |
| } |
| } |
|
|
| init(); |
|
|