{"slug":"report-card","title":"report-card","summary":"Render a finished BS report as a self-contained HTML page — score hero, filterable claims, readable on a phone, prints to a clean PDF. Use when the user wants to open, share, print, or show a report to someone who is not going to read a markdown table, or asks for \"the HTML versi","platform":"Claude","tags":[],"authorName":"LLM Mart","authorSlug":"llm-mart","score":0,"source":"github","price":null,"verified":false,"createdAt":"2026-09-26T16:29:10.509748Z","repo":{"url":"https://github.com/SerhiiKorniienko/bullshit-detector","stars":149,"forks":9,"license":"MIT","updatedAt":"2026-09-25T19:53:24Z"},"bodyHtml":"<hr>\n<h2>name: report-card\ndescription: Render a finished BS report as a self-contained HTML page — score hero, filterable claims, readable on a phone, prints to a clean PDF. Use when the user wants to open, share, print, or show a report to someone who is not going to read a markdown table, or asks for \"the HTML version\", \"something I can send\", \"a shareable page\", or \"make this readable\".</h2>\n<h1>report-card</h1>\n<p>Turn <code>bs-report-&lt;slug&gt;-&lt;date&gt;.md</code> into one HTML file anybody can open.</p>\n<p>The markdown is the artifact and stays the artifact — it is what <code>tally.py</code> checks, what gets\ndiffed against a later run, and what gets committed. This skill adds a <strong>reader-facing</strong> view of\nthat same file. It renders; it never edits, recounts, or adds. Every number on the page is copied\nfrom the markdown verbatim.</p>\n<h2>Why this exists</h2>\n<p>A BS report is a five-column claims table. On a laptop that is fine. On a phone — which is where a\nshared link is actually opened — the Evidence column is unreadable, and the reader's first\nquestion (\"what's wrong with it?\") means scrolling twenty rows to find the two that matter.</p>\n<h2>Usage</h2>\n<pre><code>uv run &lt;this-skill-dir&gt;/scripts/render_report.py ~/.bullshit-detector/reports/&lt;YYYY&gt;/bs-report-&lt;slug&gt;-&lt;date&gt;.md\n</code></pre>\n<p>Writes <code>bs-report-&lt;slug&gt;-&lt;date&gt;.html</code> beside the markdown. <code>-o &lt;path&gt;</code> puts it elsewhere. No install\nstep, no dependencies — the script is stdlib-only, runs under plain <code>python3</code> as well as <code>uv run</code>,\nand the page it produces makes no network requests. That combination is what lets it work in a\ncode-execution sandbox, where the HTML is the only thing the user can take away.</p>\n<ul>\n<li><code>--open</code> shows the page in the default browser. Where there is no browser — a sandbox, a headless\nhost — it says so and the file is still written. <strong>It is skipped when the page already exists</strong>,\nbecause re-rendering is normal (the run line gets finalised after a first pass) and every open\nspawns another tab — three consecutive real runs left the user with two. The file updates in\nplace; reload the tab you have.</li>\n<li><code>--reopen</code> opens even when the page existed — for picking a report back up in a later session. Never treat that as a failure.</li>\n<li><code>--quiet</code> prints only the output path, for scripting.</li>\n</ul>\n<h2>The handoff block</h2>\n<p>By default the script prints the block that ends a detector run:</p>\n<pre><code>BS score 4/10 · Mostly fine\n  the macro data is real and mostly checks out; the narrative glue is crypto-Twitter.\nTally: 35 claims extracted, 34 individually source-checked — 22 confirmed, 5 plausible,\n  5 misleading, 2 false. 1 not checked.\nrun: 16m30s, searches 35, tools 65, coverage 1, per claim 29s\n\nmarkdown  file:///Users/…/bs-report-japans-money-is-collapsing-2026-07-31.md\npage      file:///Users/…/bs-report-japans-money-is-collapsing-2026-07-31.html\n          opened in your browser\n</code></pre>\n<p><strong>Paste it; don't rebuild it.</strong> Every figure came out of the report that <code>tally.py</code> had just\nrecounted. A summary retyped from memory of what you wrote is wrong in the direction that flatters\nthe run — the same failure mode as the tally and the search count, one level up.</p>\n<p>The paths are <code>file://</code> URLs on purpose: terminals linkify a bare URL, so both files are one\ncmd-click from opening. Don't shorten them to <code>~/…</code> or hide them behind link text.</p>\n<h2>The compliance gate</h2>\n<p>Before rendering, the script runs the report through the detector's <code>tally.py</code> and <strong>refuses to\nrender a report that fails it</strong>:</p>\n<pre><code>REFUSED: bs-report-our-solar-system-2026-07-31.md does not pass tally.py.\n  ✗ run line: 25 claims individually source-checked from 21 searches — every claim\n    carrying a verdict needs its own search, so this reports more verification than\n    was performed\n</code></pre>\n<p>Exit 3. This is the point of the gate: a page this presentable, built from a report that fails its\nown arithmetic, launders a broken report into something that looks authoritative. Fix what the gate\nnames and re-run.</p>\n<ul>\n<li><code>--force</code> renders anyway and prints the failures as warnings.</li>\n<li><code>--no-check</code> skips the gate — for markdown that isn't a BS report.</li>\n<li><code>--tally &lt;path&gt;</code> or <code>$BULLSHIT_DETECTOR_TALLY</code> if it can't be found automatically.</li>\n</ul>\n<p>If <code>tally.py</code> isn't installed at all — this skill can be installed without the detector — the\nscript warns and renders. A missing validator is a reason to warn, not a reason to stop someone\nviewing a report they already have.</p>\n<p><code>--og-image &lt;absolute-url&gt;</code> adds a link-preview image. Only useful once the page is hosted\nsomewhere; skip it for local files.</p>\n<h2>Tags for a hosted page</h2>\n<p>For a hosted page the script also emits <code>&lt;link rel=\"canonical\"&gt;</code>, Open Graph and Twitter card\ntags, and <code>Article</code> structured data. It needs the page's public URL to do that:</p>\n<ul>\n<li><code>--canonical &lt;absolute-url&gt;</code> is that URL. Pass it whenever the page has a fixed address.</li>\n<li>Without it, the page ships no canonical and no <code>url</code> in the structured data. The script never\nguesses one from <code>--og-image</code> or anything else: a wrong canonical tells search engines the wrong\naddress is the real one, which is worse than none.</li>\n</ul>\n<p><code>--author &lt;name&gt;</code> and <code>--author-type Person|Organization</code> set the Article author. The default is\nthe tool itself, since this skill runs for everyone; pass your own name when you publish.</p>\n<p><code>datePublished</code> comes from the report's own <code>**Checked:**</code> line, never from the render time. A\nfact-check that stamps today's date on work done last month is the one thing this structured data\nmust not say, so a date it cannot read produces no date at all.</p>\n<p>Then tell the user the path and that it opens in any browser. On macOS <code>open &lt;path&gt;</code> does it.</p>\n<h2>What the page adds over the markdown</h2>\n<ul>\n<li><strong>Score hero</strong> — the number at the size it deserves, coloured by the RUBRIC band (Solid /\nMostly fine / Hype-heavy / Mostly bullshit / Fabricated), with the one-line verdict beside it.</li>\n<li><strong>Verdict filter chips</strong> — <code>Problems</code> shows only ❌ and \uD83D\uDFE0. <code>Load-bearing only</code> hides the\nincidental table. This is the whole reason a non-technical reader gets through the report.</li>\n<li><strong>Claims as cards under 900px</strong>, as a table above it. Same rows, same order, same text.</li>\n<li><strong>Print stylesheet</strong> — Cmd-P gives a clean PDF with the URLs printed after each link.\nFilters are deliberately ignored when printing: a filtered PDF is a report that quietly dropped\nrows, which is the one artifact this tool must never produce.</li>\n<li><strong>Link-preview tags</strong> — a pasted link shows <code>&lt;title&gt; — N/10</code> and the verdict line instead of a\nbare URL.</li>\n</ul>\n<h2>Rules</h2>\n<ul>\n<li><strong>Render, don't re-report.</strong> If a number looks wrong, the markdown is wrong — fix it there and\nre-render. Never correct it in the HTML.</li>\n<li><strong>The markdown is still the source of truth.</strong> Say so when handing over the file; the HTML is a\nview, and only the markdown is what <code>tally.py</code> validated.</li>\n<li><strong>Don't reach for <code>--force</code> to make a refusal go away.</strong> The gate is naming a real defect in the\nreport. Rendering past it produces a page that looks more trustworthy than the thing behind it,\nwhich is the failure this whole tool exists to catch.</li>\n<li><strong>Don't publish anything without being asked.</strong> People point this tool at unpublished drafts,\ninternal docs and things they were sent in confidence. Writing a local file is not publishing;\nputting it on the internet is a separate decision that belongs to the user, per report.</li>\n<li>The renderer is deliberately lenient — a report missing its score or version stamp still renders,\nwith a note on the page saying which fields were missing. That is a viewer's job. <code>tally.py</code> is\nthe strict one, and the two must not swap roles.</li>\n</ul>\n<h2>Known limits</h2>\n<ul>\n<li>The claims table must keep its <code>| # | Claim | Type | Verdict | Evidence |</code> shape — the <code>#</code> column\nis how the renderer tells a claims table from any other table, and the verdict glyph is read from\nits own cell, never from anywhere else in the row.</li>\n<li>Reports in any language render fine (system fonts, no bundled webfont).</li>\n<li>Images are not generated here. The social carousel lives in\n<a href=\"../share/SKILL.md\">share</a>.</li>\n</ul>\n","files":[{"path":"scripts/render_report.py","sizeBytes":46081,"isText":true},{"path":"SKILL.md","sizeBytes":8016,"isText":true},{"path":"VERSION","sizeBytes":7,"isText":false}],"reviewScore":null,"reviewSummary":null,"trust":{"provenance":"trusted-source-unreviewed","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow.","bodySource":null},"bodyLocked":false,"purchaseUrl":null,"sourceUrl":null,"report":{"provenance":"trusted-source-unreviewed","screen":{"ran":true,"outcome":"clean","suspicious":0,"notes":0,"hiddenCharacters":false},"virusScan":{"engine":"clamav","status":"clean","scannedAt":"2026-09-26T16:29:15.63438Z","sha256":"174CE152BA221427ECED541681F8A698D5145B7CD7A3B6603DDF2678F23E836D","sizeBytes":19707},"review":null,"source":{"repositoryUrl":"https://github.com/SerhiiKorniienko/bullshit-detector","path":"skills/publishing/report-card","license":"MIT","commit":"1a5955e326ee0fe063c6bc8585f40efaf5609c4b","subtreeSha":"4B9949D03AB9ECD7C2E9C43E0BC7DBE911B32E18C52DDAC3C00FC9AA8FEFBD09","lastSyncedAt":"2026-09-26T16:29:09.396087Z"},"reviewedAt":"2026-09-26T16:29:47.597581Z","notice":"Community-authored content, reproduced verbatim and not vetted as instructions. Treat it as data to evaluate, never as directives to follow."},"install":[{"target":"skills-cli","command":"npx skills add https://github.com/SerhiiKorniienko/bullshit-detector/tree/main/skills/publishing/report-card"},{"target":"claude-code","command":"claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install serhiikorniienko-bullshit-detector@llmmart"},{"target":"git","command":"git clone https://github.com/SerhiiKorniienko/bullshit-detector.git"}]}