// Renders the print-ready auditable report from the agent's last result, // passed via sessionStorage. Includes original query, planner decision, // full specialist trail, map snapshot, briefing prose with citations, // and a Sources section listing every doc_id with its vintage + URL. (function () { const raw = sessionStorage.getItem("riprap_report"); if (!raw) return; let pkg; try { pkg = JSON.parse(raw); } catch (e) { document.getElementById("paper").innerHTML = `
Could not parse stored report payload: ${e.message}
`; return; } render(pkg); })(); function escapeHtml(s) { return String(s ?? "").replace(/&/g, "&").replace(//g, ">"); } function render(pkg) { const r = pkg.result || {}; const plan = pkg.plan || r.plan || {}; const trace = pkg.trace || []; const labels = pkg.sourceLabels || {}; const urls = pkg.sourceUrls || {}; const vintages = pkg.sourceVintages || {}; const stepLabels = pkg.stepLabels || {}; const intent = r.intent || plan.intent || "—"; const intentTitleMap = { single_address: "Flood-exposure briefing — address", neighborhood: "Flood-exposure briefing — neighborhood", development_check: "Active development × flood exposure", live_now: "Current conditions — NYC", }; const place = (r.target && r.target.nta_name) || (r.geocode && r.geocode.address) || r.place || "—"; // Build the citation index from the briefing prose so we render a // numbered Sources section in the SAME order the chips appear in the // text — same idiom as the agent UI. const citeIndex = {}; const para = r.paragraph || ""; const para2 = para.replace(/\[([a-z0-9_]+)\]/gi, (_, id) => { const norm = id.toLowerCase(); if (citeIndex[norm] == null) citeIndex[norm] = Object.keys(citeIndex).length + 1; return `${citeIndex[norm]}`; }); const html = `| # | Step | Status | Elapsed | Result / error |
|---|---|---|---|---|
| ${i + 1} | ${escapeHtml(label)} ${escapeHtml(s.step)} |
${mark} | ${s.elapsed_s != null ? s.elapsed_s + "s" : "—"} | ${detail} |
This is an exposure briefing, not a damage probability or insurance rating. Tier and headline statistics are computed from a deterministic, peer-reviewed-grounded rubric (see METHODOLOGY.md in the source repository). The synthesis prose is generated by IBM Granite 4.1 in document-grounded mode; every numeric claim is verified to appear verbatim in a source document before render, and unsupported sentences are dropped.
Stack: Granite 4.1 (3b planner / 8b reconciler) via Ollama, Granite Embedding 278M for RAG over agency reports, Granite TimeSeries TTM r2 for live surge nowcast, Prithvi-EO 2.0 for satellite-derived flood polygons (offline pre-computed). Apache-2.0 across the stack. Inference runs locally on the deploying machine; no vendor LLM is contacted at runtime.
Out of scope: engineering vulnerability (foundation/structural fragility), social capacity, financial absorption, sub-surface flooding (basement apartments, subway entrances). Datasets are vintage-bounded as noted per source above.
. Keep parity with // agent.js's renderMarkdown so reports look like the live UI. function renderBriefingMarkdown(text) { const lines = text.split("\n"); const out = []; let para = []; let bullets = []; const flushPara = () => { if (!para.length) return; const safe = para.join(" ").trim().replace(/\*\*([^*]+)\*\*/g, "$1"); if (safe) out.push(`
${safe}
`); para = []; }; const flushBullets = () => { if (!bullets.length) return; const items = bullets.map(b => { const safe = b.trim().replace(/\*\*([^*]+)\*\*/g, "$1"); return `