Reports & Presentations
vita's report system turns research, analysis, meetings, and operational evidence into durable artifacts: Markdown documents, designed HTML reports, HTML slide decks, and PDFs. Files on disk are the source of truth for report content. SQLite adds lifecycle classification, keyword and semantic retrieval, interaction history, and grouping; a verified publisher can expose selected artifacts through tokenized, optionally PIN-gated URLs at vita-reports.ham.xyz.
The central design principle is:
Report content is durable because it is a file in
data/reports/; most discovery state inreports.dbcan be rebuilt from those files. Exact interaction rowsβespecially per-token attributionβcannot be reconstructed after DB loss, so deleting the database is not operationally lossless.
Not every evidence product is a report. In particular, document-flow produces a live documentation canvas, and /oneshot may choose a canvas or another proof surface. Those artifacts are adjacent to the report system but do not automatically enter reports.db.
System flow
research / meetings / production data / human brief
|
v
Markdown, HTML, slides, or PDF in data/reports/
|
+--------------+----------------+
| |
v v
Sentinel + API self-heal verified publisher
| reports.publish
v |
reports.db v
metadata + FTS5 + vectors vita-reports.ham.xyz
lifecycle + interactions token + optional PIN
|
v
/reports UI + /api/reports + reports.search CLI
There are deliberately two completion boundaries:
- Authored means the durable file exists and can be indexed locally.
- Published means
reports.publishhas fetched the share back through the public domain and verified the expected report or PIN gate. Minting a token alone is not a successful handoff.
The data model
A report is identified by a base slug, such as 2026-07-15-some-analysis. Files sharing that base are treated as formats of the same logical report:
| File | Role |
|---|---|
{slug}.md |
Markdown body with YAML frontmatter; normally the canonical format |
{slug}.html |
Designed single-file HTML version, or an HTML-only report |
{slug}-slides.html |
HTML slide-deck version |
{slug}.pdf |
PDF version, supported by public sharing |
{slug}-assets/ |
Per-report images and other assets |
The conventional filename is YYYY-MM-DD-short-description.md. The date prefix is a fallback sort key and contributes to series detection.
PDF is currently a sharing format rather than a first-class indexed family: the report index exposes has_html and has_slides, but no equivalent has_pdf field. A PDF-only artifact therefore has weaker discovery in /reports unless it also has Markdown or HTML metadata.
Frontmatter
Markdown reports carry YAML frontmatter. HTML reports carry equivalent metadata in a leading <!-- ... --> comment.
---
title: "Report Title"
date: "2026-07-15"
project: project-slug
tags: [research, strategy, analysis]
description: "Optional one-line summary."
parent: 2026-07-14-overview
---
Report content in Markdown...
The canonical indexer in scripts/reports/index_db.py parses Markdown frontmatter with yaml.safe_load. HTML comment metadata uses a simpler line-oriented key: value parser. Some API mutation paths also edit individual frontmatter lines, so simple scalar fields and inline tag lists are the most interoperable authoring style. A parent ending in .md is normalized to the bare slug.
Three ways reports relate
These mechanisms solve different problems:
| Grouping | Source of truth | Cardinality | Set by |
|---|---|---|---|
| Parent/child | frontmatter parent |
one parent per report | PATCH /{slug}/parent or file edit |
| Series | reports.db |
machine-clustered members | detect_series() during reclassification |
| Collections | data/reports/collections.json |
ordered many-to-many lists | collections API or /reports UI |
Parent/child represents one logical document split into parts. Series is heuristic clustering by project, titles, and dates. Collections are manually curated playlists and can cross project boundaries.
Where state lives
| Path or table | Contents |
|---|---|
data/reports/{slug}.* |
durable report files; source of truth for content |
data/reports/{slug}-assets/ |
per-report assets |
data/reports/reports.db |
SQLite metadata, FTS, vectors, lifecycle, changelog, series, and interactions |
reports / reports_fts |
indexed metadata and FTS5 text |
report_vectors / reports_vec |
embedding hashes and sqlite-vec vector index |
changelog |
edit history used by lifecycle classification and the UI |
interactions |
timestamped activity rows, including source and optional share token |
series / series_members |
detected report families |
data/reports/.report-state.json |
legacy pin/star/view/revision state retained for compatibility |
data/reports/.shares.json |
share-token registry: token to slug, format, PIN, and kind |
data/reports/collections.json |
durable curated collections |
data/reports/.history/{slug}/ |
backups made by the LLM editor |
The legacy state file and database coexist. Current write paths update both where compatibility requires it. Search, lifecycle ranking, changelog, semantic vectors, and token-attributed interactions live in the database; file scanning remains the content fallback when the DB is unavailable. Metadata, FTS, vectors, and lifecycle can be regenerated. Historical interaction rows and their token attribution cannot.
Generating reports
Report generation is intentionally not one universal pipeline. Choose based on the desired artifact and quality bar.
| Path | Best for | Trade-off |
|---|---|---|
generate_html_report_v2 |
substantial, polished HTML reports | multi-stage, slower, roughly $3β7 per report |
generate_html_report |
drafts and high-volume HTML | faster and cheaper, one-shot visual judgment |
generate_presentation / frontend-slides |
viewport-fitted HTML decks | slide constraints rather than document layout |
| specialized skills | WBR, engineering, offsite, or other domain-specific evidence | opinionated sourcing, review, and publishing contracts |
All core generators write into data/reports/. Publication is a separate step; use verified publishing when another person needs the public URL.
Designed HTML: v2 multi-stage pipeline
scripts/reports/generate_html_v2.py is the default for substantial designed reports. It turns a brief into a single-file HTML report plus a {slug}-assets/ directory:
- Design spec β Claude Opus 4.7 produces a structured visual and narrative specification.
- Image assets β gpt-image-2 renders the specification's image manifest in parallel.
- Implement β Opus produces complete HTML using the brief, spec, asset paths, and optional design exemplars.
- Render β Playwright captures 1440, 900, and 390-pixel-wide screenshots.
- Critique β a multimodal pass compares the renders with the design spec.
- Refine β Opus applies the critique to the HTML.
Stages 4β6 repeat when refine_iters >= 2. The module supports stage caching/resume and records token/cost usage in its result.
from scripts.reports.generate_html_v2 import generate_html_report_v2
result = generate_html_report_v2(
title="Report Title",
content="all research and source material",
project="project-slug",
tags=["research", "strategy"],
)
.venv/bin/python scripts/reports/generate_html_v2.py "Title" path/to/brief.md \
--project project-slug --tags research,strategy
Useful controls include --style-hints, --slug, --no-images, --no-refine, and --refine-iters.
Designed HTML: legacy single-shot
scripts/reports/generate_html.py sends the brief through one Gemini 3.1 Pro OpenRouter call. It is the fast, inexpensive option for drafts and repeated production. It also backs POST /api/reports/{slug}/generate-html.
from scripts.reports.generate_html import generate_html_report
result = generate_html_report(
title="Report Title",
content="source material",
style_hints="optional design direction",
project="project-slug",
tags=["draft"],
)
The practical distinction is quality control: v2 separates art direction, implementation, real rendering, critique, and refinement; legacy asks one model call to make all those decisions at once. Neither path proves factual correctness. Grounding, claim review, and publish verification remain the caller's responsibility.
Model strings and cost constants are pinned in the generator modules and will drift over time. See model guidance rather than treating this page as the model registry.
Slide presentations and frontend-slides
scripts/reports/generate_presentation.py produces self-contained HTML decks with slides fixed to 100vh/100dvh, no per-slide scrolling, keyboard/touch/wheel navigation, transitions, navigation dots, a progress bar, responsive height breakpoints, and reduced-motion handling. Output uses the -slides.html suffix and carries a presentation tag.
from scripts.reports.generate_presentation import generate_presentation
result = generate_presentation(
title="Deck Title",
content="# Slide 1\nIntro\n# Slide 2\nEvidence",
style_preset="Swiss Modern",
project="project-slug",
)
The frontend-slides skill at .claude/skills/frontend-slides/SKILL.md adds a more interactive workflow for creating decks from scratch or converting PPT/PPTX files. It uses visual style discovery, can reuse the 12 presets in STYLE_PRESETS.md, and validates that every slide fits the viewport. Use the module when the outline and aesthetic are already known; use frontend-slides when visual exploration or conversion is part of the task.
Editing an existing Markdown report
scripts/reports/edit_report.py has two modes:
- Simple (
edit_simple) β synchronous edit returned directly. - Agent (
edit_agent) β asynchronous agent edit returning a task ID to poll.
Both back up the previous file to data/reports/.history/{slug}/{timestamp}.md and append a changelog entry. The API exposes these through POST /{slug}/edit and GET /{slug}/edit/{task_id}.
Indexing and lifecycle
Autonomous indexing
scripts/sentinel/report_sentinel.py is a Sentinel ScriptResponder for filesystem changes under data/reports/:
watchdog event -> SentinelLoop -> report_sentinel
-> hash files -> two-tick stability check
-> index stable changes
The stability check avoids reading a report while it is still being written. A stable change updates metadata, diff/changelog information, FTS content, and lifecycle fields.
GET /api/reports is a second, rate-limited backstop. Every 30 seconds at most, it finds unindexed Markdown/HTML files and synchronizes derived changelog fields. It also reconciles has_html and has_slides for already-indexed rows, so adding a new format to an existing report no longer requires deleting and rebuilding its row.
When newly discovered files are indexed, the API launches a detached semantic backfill. Otherwise it throttles semantic refresh to roughly hourly. Content hashes prevent unchanged reports from being re-embedded.
Lifecycle classification
scripts/reports/lifecycle.py evaluates stages in this order; first match wins:
| Stage | Rule |
|---|---|
active |
edited within 7 days, or at least 3 interactions in 7 days |
living |
at least 3 changelog entries on at least 3 distinct days |
reference |
at least 10 views and no edit in 30 days |
recent |
created within 14 days |
archive |
everything else |
Manual lifecycle_override always wins. A final NULL sweep ensures a failed per-row classification cannot leave reports invisible: unclassified reports younger than 14 days become recent; older ones become archive.
Relevance scoring combines recency, interaction, edit frequency, and manual state, with title-sensitive half-lives. Meeting-prep and recurring-meeting artifacts decay quickly; runbooks and reference material decay much more slowly. Dashboard, library, search, and project views apply different ranking emphases.
Series detection
scripts/reports/series.py groups reports within a project by normalized title/date patterns and Jaccard similarity. A cluster needs at least three related reports. Reclassification rewrites series and series_members, and API responses expose series_key, series_name, series_position, and series_total.
Retrieval and discovery
The reports subsystem has its own retrieval path. It is distinct from Vita's broader long-term-memory search.
Keyword, semantic, and smart search
scripts/reports/search.py supports three modes:
| Mode | Behavior |
|---|---|
keyword |
FTS5 prefix matching with BM25 weighting |
semantic |
cosine-nearest-neighbor search over report embeddings |
smart |
reciprocal-rank fusion of keyword and semantic results; default |
Keyword ranking weights title most heavily, followed by tags, description, body content, and changelog. Search responses can include a highlighted snippet, score, and match_type so the UI can explain why a result appeared.
Structured operators can be combined with free text:
| Operator | Examples |
|---|---|
| project | project:vita, proj:rwgps, p:cadence |
| tags | tag:strategy, tags:research |
| lifecycle | lifecycle:active, stage:reference |
| format | format:html, type:slides |
| dates | after:2026-07-01, since:2026-07-01, before:2026-08-01 |
| state | is:starred, is:pinned |
A filter-only query returns matching rows ordered by date. The CLI talks directly to SQLite and therefore remains useful when the API or web app is down:
PYTHONPATH=scripts uv run python -m reports.search \
'capabilities project:vita after:2026-07-01' --mode smart --limit 5
PYTHONPATH=scripts uv run python -m reports.search \
'lifecycle:reference tag:runbook' --json
Results include slug, title, date, project, score, match type, snippet, and the absolute report path.
Semantic index
scripts/reports/semantic.py stores 1,536-dimensional text-embedding-3-small vectors in the same reports.db using sqlite-vec. Each embedding is derived from title, description, and stripped report body, capped at 7,000 characters with a smaller retry on token overflow.
The semantic index is a deliberate dual write: metadata lives in report_vectors; vectors live in reports_vec. Backfill batches up to 50 reports, falls back to individual requests if a batch fails, skips unchanged content by hash, and prunes vectors for deleted reports.
uv run python -m scripts.reports.semantic backfill
uv run python -m scripts.reports.semantic backfill --force
uv run python -m scripts.reports.semantic search 'why was this architecture chosen?'
If sqlite-vec or embedding access is unavailable, smart search degrades to keyword-only and emits a warning instead of making the whole report library unavailable.
API surface
The internal API runs on port 33800 (./run api). api/src/routers/reports.py is mounted at /api/reports; collections and public sharing are separate routers.
Static endpoints such as /search, /stats, and /reclassify must remain declared before the /{slug} catch-all.
/api/reports
| Method | Endpoint | Purpose |
|---|---|---|
| GET | `` | List reports; filters include lifecycle, query, view, tag, and project; q uses smart search |
| GET | /search?q=&mode=smart |
Ranked smart, keyword, or semantic retrieval |
| GET | /stats |
Counts by lifecycle/project plus activity summaries |
| POST | /reclassify |
Reclassify lifecycle and detect series; called by /sleep |
| GET | /{slug} |
Content, metadata, revisions, and recent changelog |
| GET | /{slug}/html |
Serve raw HTML and record an html_serve interaction |
| GET | /{slug}/assets/{filename} |
Serve a path-traversal-guarded report asset |
| GET | /{slug}/changelog |
Paginated changelog |
| PATCH | /{slug}/state |
Set pinned/starred |
| PATCH | /{slug}/project |
Update project frontmatter |
| PATCH | /{slug}/parent |
Set or clear parent frontmatter |
| PATCH | /{slug}/lifecycle |
Set or clear lifecycle override |
| POST | /{slug}/interact |
Record a web_view interaction on the base slug |
| POST | /{slug}/generate-html |
Asynchronously generate legacy HTML from Markdown |
| POST | /{slug}/presentation |
Asynchronously generate slides from an existing report |
| POST | /{slug}/edit |
Run a simple or agent-mode Markdown edit |
| GET | /{slug}/edit/{task_id} |
Poll an agent edit |
| DELETE | /{slug}/format/{fmt} |
Delete HTML or slides while retaining the Markdown primary |
ReportMeta includes frontmatter plus derived fields: format flags, excerpt, cover image, lifecycle, relevance, view/edit counts, activity sparkline, timestamps, parent/children, collections, series, public-share status and PIN, and search fields (snippet, search_score, match_type). ReportContent additionally includes content, revisions, and changelog rows.
Collections API
/api/collections is backed by scripts/reports/collections.py and collections.json, not SQLite.
| Method | Endpoint | Purpose |
|---|---|---|
| GET / POST | /api/collections |
list or create collections |
| GET / PATCH / DELETE | /api/collections/{id} |
inspect, update, reorder, or delete |
| POST / DELETE | /api/collections/{id}/members/{slug} |
add or remove a member |
Updating members is a full ordered replacement, which is also how collections are reordered.
Verified public publishing
The agent-facing publishing primitive is scripts/reports/publish.py. Report-producing workflows should use it instead of hand-written curl calls.
PYTHONPATH=scripts uv run python -m reports.publish 2026-07-15-report-slug \
--format html --pin 1234
PYTHONPATH=scripts uv run python -m reports.publish \
--verify-token EXISTING_TOKEN
Supported formats are primary, markdown, html, slides, and pdf. --pin "" explicitly clears an existing PIN; --notify queues a Telegram link only after verification. Publishing is idempotent for the same slug and format, so updating a live report does not mint a new token or break its URL.
The publisher verifies the public domain, not merely localhost:
- POST the share request to the local API.
- GET
https://vita-reports.ham.xyz/s/{token}, retrying one transient network failure. - For open shares, require a nontrivial body and a report-title marker.
- For PIN shares, require the gate markers and confirm that the report title did not leak. The verifier never submits or logs the PIN.
Failures are attributed to the failing leg: report/report-file, local API, tunnel/public API, or content verification. A public 404 is checked against the same token on the local API to distinguish a dangling share from stale public infrastructure. The command emits one JSON result containing slug, token, url, pin, verified, and per-leg checks.
--no-verify exists for exceptional debugging, but it does not satisfy the normal publish contract.
Through-the-domain smoke
scripts/reports/domain_smoke.py validates the whole share surface:
- Offline: cross-reference every report token in
.shares.jsonagainst files on disk and classify valid, orphaned, format-dangling, and malformed records. - Network: sample current open shares across formats and one PIN share through the public domain.
- Freshness: compare the domain health endpoint's git SHA/code freshness with local HEAD so stale code cannot masquerade as a successful content test.
PYTHONPATH=scripts uv run python -m reports.domain_smoke
PYTHONPATH=scripts uv run python -m reports.domain_smoke --offline-only
PYTHONPATH=scripts uv run python -m reports.domain_smoke --token TOKEN
./run smoke-reports
--strict-dangling turns historical dangling entries into failures. --network-optional is used by fast verification so a down stack is a visible skip rather than a false content failure. Service monitoring uses --freshness-only --grace-minutes 30.
Public sharing details
api/src/routers/shared_reports.py serves individual reports and collections on vita-reports.ham.xyz. The registry is data/reports/.shares.json.
POST /api/reports/{slug}/share?format=primary&pin=1234
-> { token, url: "https://vita-reports.ham.xyz/s/{token}" }
Sharing a parent also shares its children. A corrupt registry is an honest HTTP 503; an unknown token is a plain 404; a valid token whose file has disappeared returns a styled 404 tombstone. These distinctions matter operationally because they separate infrastructure failure, invalid links, and rename/deletion drift.
PIN gate
- PINs are stored plaintext in
.shares.json; this is a convenience gate, not high-assurance authentication. - Before unlock,
GET /s/{token}returns only the gate and sends no report data. POST /s/{token}/unlockcompares the PIN server-side, keeping it out of URLs and access logs.- A successful unlock sets a 180-day, path-scoped, httpOnly,
SameSite=Laxcookie; it isSecureunder HTTPS. - The unlock key is never exposed to page JavaScript or placed in the URL.
Rendering
- Markdown shares render as responsive standalone pages with a table of contents when there are at least two headings, print styles, dark/light support, and copy-Markdown control.
- Links to another shared Markdown report are rewritten to that report's share URL.
- HTML shares receive a
<base href="/s/{token}/">so relative assets resolve under the token. - Slides serve
{slug}-slides.htmldirectly; PDFs are served inline. primarychooses HTML when present, then Markdown.
Interactions and token attribution
reports.db.interactions records report_slug, timestamp, source, and optional share token. Current sources include:
web_viewfromPOST /api/reports/{slug}/interacthtml_servefrom the internal HTML routepublic_shareafter public content is actually servedshare_unlockafter a correct PIN
The token answers βwhich link was opened?β when multiple links point to the same report. Interaction writes on the public path are best-effort and never block delivery. A gated report may record share_unlock followed by public_share when the browser reloads after unlocking; viewing the locked gate alone is not counted as a report open.
The raw attribution exists, but there is not yet a dedicated token-analytics API or UI. Querying or aggregating it currently requires direct database work.
Collections
POST /api/collections/{collection_id}/share
DELETE /api/collections/{collection_id}/share
GET /c/{token}
The public collection page preserves curated member order. Sharing ensures each member has a primary report token. Tokens created only for a collection are marked via_collection so unsharing can remove them unless another collection or an individual share still needs them.
Web UI (/reports)
web/src/routes/reports/ implements the report library and viewer on the web app (port 33801). The current surface includes:
- dashboard, gallery, list, and mobile layouts
- lifecycle, project, tag, type, and state filtering
- server-backed hybrid search with snippets and match labels
- covers, excerpts, format/lifecycle badges, and activity metadata
- Markdown/HTML report viewing, document-title updates, and report actions
- collection creation, membership, reordering, and sharing
The internal UI exposes share_pin for owner convenience. That makes /reports an administrative surface; it is not suitable for untrusted viewers.
Specialized evidence pipelines
These workflows are documented here because they produce reports or closely related evidence, but their domain logic belongs in their skill files.
document-flow: shipping-flow documentation
The exact surface name is document-flow, defined at .claude/skills/document-flow/SKILL.md. It documents an existing, shipping product flow rather than building or validating a proposed change.
Its output is a live documentation canvas published at /cv/{token}, not a file in data/reports/. A complete flow combines:
- every user-visible state at desktop and mobile widths
- real screenshots, not mockups
- live Amplitude volume/funnel/property-split measures
- a deep link from every metric card to the exact chart supporting it
- event coverage and instrumentation gaps
- methodology and denominator definitions
Totals, unique users, and funnel conversion answer different questions, so the metric contract is chosen explicitly before authoring. Instrumentation gaps remain visible rather than being papered over. Use document-flow when the artifact must remain a live map of a shipping experience; use a report when the output is primarily a durable narrative or analysis.
engineering-report: contribution weight analysis
The exact surface name is engineering-report, at .claude/skills/engineering-report/SKILL.md. It compares named engineers by reading every merged PR diff and assigning engineering weight based on difficulty, judgment, debugging, design, and riskβnot lines changed.
The workflow normalizes by available working days from a fresh Deel out-of-office export, calibration-audits scoring across engineers, creates Markdown plus a terse HTML report, and publishes the HTML through verified, PIN-gated reports.publish.
Its limits are material: it sees GitHub-visible work, depends on a fresh manual OOO export, can be affected by PR-splitting behavior, and becomes expensive/rate-limited at large PR volumes. Scores are decision support, not an objective measure of human value.
wbr: Weekly Business Review
The exact surface name is wbr, at .claude/skills/wbr/SKILL.md. It creates a best-attempt first-draft Weekly Business Review for the L10, then refines it collaboratively.
The workflow retrieves prior editions and templates with reports.search, gathers current PR, support, growth, canvas, offsite, and working-memory evidence, renders an HTML report with traceable links, and publishes it through reports.publish. Sensitive strategy or personally identifying detail requires a PIN. Edits update the same file and therefore retain the same share token.
offsite-report: operational recap plus data validation
The exact surface name is offsite-report, at .claude/skills/offsite-report/SKILL.md. It turns a long multi-speaker recording into two coordinated artifacts:
- an operational recap with decisions, rocks, owners, tensions, and unresolved issues; and
- a supplemental production-data report that tests claims against live business data.
The workflow uses diarized transcription, a human speaker-map checkpoint, map-reduce extraction, editorial synthesis, adversarial review, and a human content checkpoint. scripts/meetings/build_offsite_report.py and build_data_report.py render the approved data. Both outputs are verified through the domain and normally PIN-gated.
/oneshot: evidence-first high-stakes work
.claude/skills/oneshot/SKILL.md is an opt-in, high-stakes plan -> work -> verify -> present workflow. It is not another report generator. Its presentation stage chooses the evidence surface appropriate to the work: a Cadence artifact for RWGPS, or a Vita canvas/report-like artifact elsewhere. Real screenshots, primary-source evidence, and adversarial review are required. Only outputs actually written into data/reports/ enter this subsystem.
CGM metabolic PDF
scripts/reports/cgm_report.py generates a four-page PDF from local CGM data: executive dashboard, daily profiles, variability analysis, and patterns/insights. The composite metabolic score weights time in range (35%), coefficient of variation (25%), mean glucose (20%), time below the low threshold (10%), and MAGE (10%).
uv run python scripts/reports/cgm_report.py
uv run python scripts/reports/cgm_report.py --recipient "Dr. Example"
uv run python scripts/reports/cgm_report.py --start 2026-07-01 --end 2026-07-07
CGM data is health-sensitive. Generate locally and use PIN-gated PDF sharing when a public-domain handoff is necessary.
Candid limitations
- Files and DB can diverge briefly. Sentinel, the 30-second list self-heal, format reconciliation, and semantic backfill reduce drift; they do not make a multi-store transaction.
- Database deletion is content-safe, not history-safe. The file library and most indexes can be reconstructed, but detailed interaction/token rows cannot.
- Semantic search is an optional external dependency. It requires embedding access and
sqlite-vec; keyword search is the designed fallback. - Semantic bodies are truncated. Very long reports are represented by title, description, and the first bounded body segment, not every paragraph.
is:sharedis incomplete. The parser/UI vocabulary recognizes it, but ranked-row filtering currently has no shared field to enforce. Use the API'ssharedmetadata or inspect.shares.jsonuntil this is fixed.- PDF discovery is weak. Sharing supports PDF, while the core index and format filters focus on Markdown, HTML, and slides.
- Interaction analytics are raw. Token attribution is stored but lacks an aggregate endpoint, bot filtering, unique-view semantics, or a first-class UI.
- PINs are convenience gates. They are plaintext at rest and should not be treated as identity, authorization, revocation auditing, or high-security access control.
- Share registry cleanup is not automatic. Domain smoke finds orphaned and format-dangling entries; historical drift may remain until explicitly repaired.
- Generated polish is not factual verification. The v2 visual critique checks design against a spec, not claims against primary sources. Specialized workflows add their own evidence and human checkpoints.
- document-flow canvases are a separate system. They are public/live evidence pages but are not indexed, searched, classified, or collected as reports.
Maintenance and file map
| Path | Role |
|---|---|
scripts/reports/generate_html_v2.py |
multi-stage designed HTML generator |
scripts/reports/generate_html.py |
legacy single-shot HTML generator |
scripts/reports/generate_presentation.py |
HTML slide generator |
scripts/reports/edit_report.py |
simple and agent-mode Markdown editor |
scripts/reports/index_db.py |
SQLite schema, parsing, FTS, interactions, and indexing |
scripts/reports/search.py |
structured keyword/semantic/smart retrieval and CLI |
scripts/reports/semantic.py |
vector schema, embedding backfill, and KNN search |
scripts/reports/lifecycle.py |
lifecycle and relevance classification |
scripts/reports/series.py |
automatic series detection |
scripts/reports/collections.py |
durable collections store |
scripts/reports/publish.py |
idempotent, through-domain verified publisher |
scripts/reports/domain_smoke.py |
registry, content, PIN-gate, and code-freshness smoke |
scripts/reports/cgm_report.py |
CGM PDF builder |
scripts/reports/render.py |
Markdown render helper |
scripts/reports/similarity.py |
similarity helpers for series detection |
scripts/reports/backfill_descriptions.py |
one-shot description backfill |
scripts/reports/backfill_markdown.py |
one-shot Markdown-body backfill |
scripts/sentinel/report_sentinel.py |
filesystem-event report indexer |
scripts/meetings/build_offsite_report.py |
approved offsite operational report renderer |
scripts/meetings/build_data_report.py |
offsite data-validation report renderer |
scripts/integrations/openai_image.py |
gpt-image-2 wrapper used by HTML v2 |
api/src/routers/reports.py |
report API, search, and index self-heal |
api/src/routers/report_collections.py |
collections API |
api/src/routers/shared_reports.py |
report/collection share routes and interactions |
web/src/routes/reports/ |
report library, search, viewer, and collections UI |
.claude/skills/document-flow/SKILL.md |
shipping-flow documentation canvas workflow |
.claude/skills/engineering-report/SKILL.md |
engineering contribution report workflow |
.claude/skills/wbr/SKILL.md |
Weekly Business Review workflow |
.claude/skills/offsite-report/SKILL.md |
offsite report workflow |
.claude/skills/frontend-slides/SKILL.md |
interactive presentation workflow |
.claude/skills/oneshot/SKILL.md |
high-stakes evidence workflow |
data/reports/ |
report files, assets, DB, shares, collections, and history |
Where to go next
- Sentinel β filesystem events and autonomous indexing.
- Model guidance β current model selection; generator constants drift.
- Research and Deliberation β common report-content producers.
- Search β Vita's broader long-term-memory search, distinct from report retrieval.