Help us improve
Share bugs, ideas, or general feedback.
From spotlight
Drafts three journalist-grade deliverables from verified findings: findings-report.md, report.html (from template), and evidence-map.json. For use after Gate 1 approval in Phase 5.
npx claudepluginhub buriedsignals/spotlight --plugin spotlightHow this skill is triggered — by the user, by Claude, or both
Slash command
/spotlight:report-draftingThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
You are at Phase 5. Gate 1 has approved a set of verified findings. Your job is to ship the three deliverables editors actually read.
Generates a self-contained HTML review artifact with structured feedback support for investigation cycles. Accepts feedback as downloadable JSON and re-triggers investigator processing.
Plans and writes long-form investigative journalism pieces with document analysis, source development, and multi-stage verification.
Generates structured Markdown research report from prior phase outputs like brainstorm, plans, code, tests, and plots. Integrates visuals, generates missing plots, verifies claim-evidence integrity.
Share bugs, ideas, or general feedback.
You are at Phase 5. Gate 1 has approved a set of verified findings. Your job is to ship the three deliverables editors actually read.
This skill instructs; you execute. Read references/report-template.html before drafting — it is the working skeleton.
Every report.html MUST include this notice at the top of the page, after the byline/header material and before the TL;DR findings table:
AI assistance notice: Spotlight is designed to help surface, organize, and cross-check information, but AI can make mistakes. You are responsible for verifying sources, confirming authenticity, assessing risks, and deciding what is publishable.
Use the template's .honesty block for this notice. Do not rewrite or soften the responsibility language.
| File | Audience | What it is |
|---|---|---|
case/findings-report.md | Editor / fact-checker | Narrative audit document. One section per finding. Plain prose + tables. The authoritative claim-by-claim record. |
case/report.html | Publication / external reader | Public-facing journalism artifact. Styled, scannable, with inline replication paths and archived primary sources. |
case/evidence-map.json | Audit / replication | Machine-readable ledger: claim → cards → query hashes → external URLs. See references/evidence-map-format.md in the data-detective skill. |
The HTML is not a markdown render. It is a designed document. Build it from the template, not from scratch.
Every <section class="finding"> MUST contain, in order:
<h2> with embedded finding ID + .pill-novel (purple, for genuinely new evidence) OR .pill-connected (outline, for new framings of public facts), plus .pill-high / .pill-med / .pill-low for confidence.<p class="deck"> (≤60ch).<div class="stats"> for findings with quantitative spine.<p> (auto-constrained to ≤72ch via column width).<div class="path" aria-label="How we got here"> — REPLICATION PATH. One .step + .what pair per phase that produced this finding. Cite SQL hashes, script paths, archived URLs. This block is what makes the finding auditable in under a minute. Mandatory.<div class="sources"> — primary-source URLs with archive references. Mandatory.Optional add-ins:
<div class="flag"> for legal qualifications — use <span class="flag-label"> for the in-line label, NOT <strong style="display:block">.<div class="timeline"> for chronological evidence chains (4-column grid: date, event, source).<div class="pull"> for a 1-2 sentence pull quote inside the finding body.The methodology section serves a dual purpose: it documents the skill (the algorithm) AND it logs the actual run. It is NOT a separate generic methodology. It is the audit trail of THIS investigation, in phase order.
Structure: one <div class="phase"> per phase (P0 through P7).
Critical: do NOT break the adversarial fact-check verdict table and the spotlight-handoff outcomes table into separate top-level sections. They read out of phase order. Instead:
<div class="phase">.<div class="phase">.This way a reader scrolling the methodology gets the full run in phase order: ingest → resolve → detect+factcheck → gate → synthesize → handoff → vault. Past investigations broke these out and the result read out-of-order; the restructuring at the end is what gave the report its final shape.
CSS variables already in the template (--ink, --paper, --rule, --bg-soft, --red, --mono, --sans, --serif). Do not reinvent them per finding.
Max-width rules (the template enforces these; do not override per-element):
h1 → 28chh2 → 32ch.deck (subhed) → 60ch<p> → constrained by the column, max-width:none.lede → constrained by the column, max-width:none.stats / .path / .sources / .flag / .timeline / .phase → full column width, no max-widthPill semantics:
.pill-novel (purple background) — genuinely new evidence not previously published..pill-connected (outline) — new framing of public facts via cross-corpus join..pill-verified (green) — fact-checker confirmed..pill-partial (amber) — fact-checker partial verdict..pill-high / .pill-med / .pill-low — confidence levels..pill-id (mono, light) — finding ID badge.TL;DR table at the top: one row per finding, <a href="#c-NNN"> linked, with novelty + confidence pills inline.
NEVER run greedy regex substitution on the HTML file. Use Read + Edit with anchored old_strings only. A greedy re.sub destroyed the entire report.html mid-pass in a prior investigation and forced a full rebuild.
Specifically:
old_string on the closing element of the prior block + the opening of the target block.Edit call.re.sub(..., re.DOTALL) on the whole file.The synthesis layer must NEVER originate a primary-source citation. Every UUID, every external URL, every filing reference, every direct quote MUST be copied verbatim from a ground-truth file written by an earlier phase. If a citation is not already in the trail, do not invent it — go fetch it.
This is the same class of rule as Firecrawl-only. The failure mode it prevents: a synthesis pass that "looks right" but contains URLs and UUIDs the LLM generated from semantic memory, that 404 or resolve to the wrong filing under adversarial review. This is the most common way investigative-journalism submissions get killed.
case-trace/spotlight/results/*/research/*.md — the literal scraped page text. URL of the original is in the filename or in the file's frontmatter or in case-trace/spotlight/results/*/investigation-log.json under urls_accessed.case-trace/spotlight/results/*/data/findings.json — the Spotlight investigator's curated source list per finding (the external_sources arrays).case-trace/data-detective/cards/senate_filing_<UUID>.md — evidence cards for primary filings, generated deterministically from the DuckDB index. The UUID in the filename IS the canonical UUID.case-trace/data-detective/anomalies/*.provenance.json — SQL hashes and detector SQL.case-trace/data-detective/external/factcheck/* — adversarial fact-checker archives.For each citation, run a verification step:
# Pattern A — UUID is a Senate LDA filing
grep -rln "<UUID>" case-trace/spotlight/results/ case-trace/data-detective/cards/
# Must return at least one ground-truth file. If empty: STOP. Do not paste this UUID into the draft.
# Pattern B — external URL (news article, gov page, etc.)
grep -rln "<URL>" case-trace/spotlight/results/
# Must return at least one ground-truth file. If empty AND the URL is not already in case-trace/data-detective/external/, STOP.
# To add a new URL: firecrawl-scrape it first, write the result under case-trace/data-detective/external/, then it is grep-able.
If a fact you want to cite has no ground-truth file, you have two options:
case-trace/data-detective/external/<slug>.md, then cite it. Never paraphrase or "remember" a URL.nytimes.com/<year>/<month>/<day>/<section>/<slug>.html guess. NYT URLs are not predictable from headline. Look it up.Before declaring P5 complete, run a closure script:
# Extract every UUID and external URL from the three drafted files
grep -ohE '[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}|https?://[^[:space:])"]+' \
case/findings-report.md case/report.html case/evidence-map.json \
| sort -u > /tmp/p5-citations.txt
# For each one, confirm it appears in ground-truth files
while read -r token; do
if ! grep -rlq -- "$token" case-trace/spotlight/results/ case-trace/data-detective/cards/ case-trace/data-detective/external/ case-trace/data-detective/anomalies/; do
echo "ORPHAN CITATION: $token"
fi
done < /tmp/p5-citations.txt
An orphan citation is a P5 bug. Fix it before declaring P5 complete — either by fetching the source (firecrawl scrape into external/) or by removing the claim from the draft.
When you correct a previously-published citation, leave a trail:
"description": "Akin Gump LDA filing for Ant Group: UUID a4411100-... (Q1 2025 LD-2). Previous version of this entry cited UUID 3a6e17c0-... in error — that UUID resolves to a Posco America filing. Citation corrected against Spotlight OS-002 evidence archive at case-trace/spotlight/results/OS-002.../research/lda-akingump-antgroup-filing.md."
This is what makes the case-trace defensible to an editor: not "we never made mistakes" but "we caught and corrected the ones we made, with the audit trail in the artifact."
1. Read references/report-template.html. Copy to case/report.html.
2. Fill the header (title, deck, byline, lede), keep the required AI assistance notice directly below it, and fill the TL;DR table from findings.json.
3. BEFORE drafting any finding, extract its citation manifest:
- From data-detective: findings.json supporting_cards + external_sources + supporting_query_hashes
- From spotlight (if promoted_from is set): case-trace/spotlight/results/<OS-NNN>/data/findings.json external_sources + research/*.md filenames + investigation-log.json urls_accessed
- Write the manifest to /tmp/c-NNN-citations.txt — this is the ALLOWED set for this finding.
- Any URL or UUID you want to put in the draft must appear in this file. No exceptions.
4. For each verified finding in findings.json:
a. Drop a <section class="finding"> from the template's finding-stub block.
b. Fill the H2 + pills (novelty inferred from finding's promoted_from + corroboration; confidence from fact-check verdict).
c. Write the body (3-6 paragraphs, prose). Quote primary-source text via Read of the archived page, never paraphrase from memory.
d. Insert the .path block — one .step+.what pair per phase that produced this finding. Cite SQL hashes from anomalies/*/provenance.json. Cite scripts. Cite archived URLs from /tmp/c-NNN-citations.txt only.
e. Insert the .sources strip — primary-source URLs only (not secondary commentary), all from the citation manifest.
5. Fill methodology section:
a. One .phase block per executed phase (P0..P7).
b. INSIDE Phase 3: adversarial fact-check verdict table.
c. INSIDE Phase 6: spotlight-handoff outcomes table.
6. Fill "Open monitoring targets" section from findings.json's unresolved-gaps list.
7. Fill footer: conflicts of interest, database attributions.
8. Write findings-report.md in parallel (narrative form, no styling, every claim sourced from the same allowed-set manifest as the HTML).
9. Write evidence-map.json (audit ledger, see data-detective/references/evidence-map-format.md).
10. RUN THE CITATION CLOSURE SCRIPT (see Citation Discipline section above). Every UUID and external URL in the three drafted files must trace to a ground-truth file. Fix orphans before proceeding.
11. Validate HTML tags balance via:
python3 -c "from html.parser import HTMLParser; ..."
12. Open report.html in browser; visual smoke test.
13. Append synthesis_complete + draft_paths + citation_closure_passed to investigation-log.json.
Reads:
case/data/findings.json (verified claims)case/data/fact-check.json (adversarial verdicts)case/data/investigation-log.json (phase log + handoff outcomes)case/anomalies/*/provenance.json (SQL hashes for path blocks)case-trace/spotlight/results/*/ (OSINT investigation outputs to summarize in Phase 6 table)Writes:
case/findings-report.mdcase/report.htmlcase/evidence-map.json.path block. If you find yourself writing "we ran D11 then drilled then archived three URLs" in the prose, lift it out into the path block..sources strip. Readers should never have to scroll to a bibliography..flag strong { display: block }. Breaks inline legal citations to new lines. Use <span class="flag-label"> instead.pandoc output, restart from the template.Read + Edit with anchored old_strings. A greedy substitution will destroy hours of work..pill-connected (outline), not .pill-novel (purple). The novel sub-element — typically a cross-corpus join or a specific lobbyist's institutional history — should be called out explicitly in a "Novelty" paragraph at the top of the finding body. Prize panels read the novelty framing first; mislabeling a NYT-reported timeline as "novel" is a credibility hit.references/report-template.html is a working skeleton with:
<head> block with the typography stack (Inter / EB Garamond / JetBrains Mono via system fallbacks)Copy it, fill it, ship it.