メインコンテンツまでスキップ

HTML Output Format (Self-contained Report)

River Review emits a self-contained HTML report when you pass --output html (CLI) or output_format: html (GitHub Action). The CSS is inlined in a <style> block and no external stylesheet or script is loaded, so the file can be stored as a CI artifact and opened directly in a browser.

Supported commands​

The CLI accepts text|markdown|json|yaml|html for --output globally, but only a few commands actually render html.

Command--output htmlProduces
river run <path>SupportedReview report (decision banner, score, findings, risk assessment)
river runs diff <id1> <id2> [<id3>...]SupportedLoop dashboard (loop signal, churn, oscillation timeline)
river review plan / river review execRejected, exit 3Use json or markdown
river evolve aggregate / river evolve replayRejected, exit 1Use text or json
river skills <path>Rejected, exit 1Use text, markdown, or json
Everything else (river review route, river runs list, river runs digest, river promote, …)IgnoredFalls back to that command's default output

The rejecting commands print:

$ river review plan --output html
Error: Unsupported output format "html" for river review. Expected: json | markdown (text not yet implemented).

$ river evolve aggregate --output html
Unsupported --output for evolve aggregate: html. Use: text | json

$ river skills . --output html
Unsupported --output for skills: html. Use: text | markdown | json

river skills used to accept yaml and html and return JSON regardless (#1705). Rejecting them is the fix for that declaration mismatch, so a caller that passed yaml to get JSON must pass json instead.

CLI​

River Review is not published to npm, so the CLI is run inside the repository with npm run river -- ....

npm run --silent river -- run . --output html > review-report.html

The river run header (a few lines starting with River Review (local)) goes to stderr for every format other than text (#1695). Stdout for html starts at <!DOCTYPE html> and ends at </html>, so redirecting it yields a valid HTML file directly.

--silent is needed only to suppress the npm run banner (> river-review@x.y.z river). npm writes that banner to stdout itself and River Review cannot control it. Invoking the CLI directly needs no flag:

./src/cli.mjs run . --output html > review-report.html

The loop dashboard is generated from two or more stored run records (.river/runs/). Oscillation detection needs three or more run ids; with exactly two the oscillation timeline is empty. Its stdout is likewise HTML only.

npm run --silent river -- runs diff <run-id-1> <run-id-2> --output html > loop-dashboard.html

GitHub Action​

- uses: s977043/river-review/runners/github-action@v1.22.0
with:
output_format: html

The action runs river run and posts its stdout as the PR comment body. GitHub strips <style> and similar tags from comments, so an HTML document does not render as intended there. Set comment: false when using html and consume the output from the job log or an artifact instead.

Sample output (review report)​

Structure of the document produced by formatHtmlOutput (the inline CSS is elided).

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>River Review Report — midstream</title>
<style>
/* inline CSS (elided) */
</style>
</head>
<body>
<h1>River Review Report</h1>
<p class="meta">
Phase: <strong>midstream</strong> &nbsp;|&nbsp; Timestamp:
<strong>2026-04-18T00:00:00Z</strong>
</p>
<div class="banner" style="background: #fff8e1; border-color: #f9a825">
! Human Review Recommended
</div>
<h2>Summary</h2>
<div class="counts">
<span class="count-chip" style="background: #d32f2f">critical: 0</span>
<span class="count-chip" style="background: #e65100">major: 1</span>
<span class="count-chip" style="background: #f9a825">minor: 0</span>
<span class="count-chip" style="background: #1565c0">info: 0</span>
</div>
<h2>Score</h2>
<div class="overall-wrap"><span class="overall">96/100</span></div>
<table>
<tr>
<th>Axis</th>
<th>Score</th>
<th style="width: 200px">Bar</th>
</tr>
<tr>
<td>パフォーマンス</td>
<td style="text-align: right">80</td>
<td>
<div class="score-bg"><div class="score-bar" style="width: 80%"></div></div>
</td>
</tr>
</table>
<h2>Findings</h2>
<table>
<tr>
<th>Severity</th>
<th>File:Line</th>
<th>Title</th>
<th>Message</th>
<th>Suggestion</th>
</tr>
<tr>
<td><span class="sev" style="background: #e65100">major</span></td>
<td><code>src/Repository/OrderRepository.php:128</code></td>
<td>N+1 query in loop</td>
<td><pre>Eager load relations</pre></td>
<td><pre>Use with()</pre></td>
</tr>
</table>
</body>
</html>

Sections appear in this order:

  • Header: phase and timestamp
  • Decision banner: colour-coded auto-approve / human-review-recommended / human-review-required
  • Summary: one count chip per severity
  • Score: overall plus the five axis bars (axis labels are Japanese)
  • Findings: a table of severity, file:line, title, message, and suggestion (or a "no findings" line)
  • Risk Assessment: present only when the plan carries riskAssessment

Sample output (loop dashboard)​

river runs diff --output html calls formatLoopDashboardHtml, whose sections differ from the review report:

  • Header: number of runs and the chain of run ids
  • suggestedLoopSignal banner: CONVERGED / REVISE_REQUIRED / ESCALATE_HUMAN / STOP_OSCILLATED / NO_SIGNAL
  • Churn: new / resolved / persisting / oscillated count chips
  • Oscillation timeline: ● (present) and ○ (absent) per finding across runs
  • New findings / Resolved findings: severity, file, and title

Notes​

  • Self-contained: CSS is inlined and nothing external is referenced, so the single file can be shared or archived as-is.
  • HTML escaping: every finding-derived string (file, title, message, suggestion, run id) is escaped, so a <script> tag in the reviewed code does not execute when the report is opened.
  • Scores are derived: the overall and per-axis scores come from the same deterministic scoring engine as the YAML and JSON output, not from an LLM judgement. Do not decide a merge on them alone — see the scoring model in YAML Output Format.
  • Japanese is hard-coded: <html lang="ja"> and the Japanese axis labels are fixed and are not switched by the review-language setting.
  • src/lib/output-formatters/html.mjs — formatHtmlOutput / formatLoopDashboardHtml implementation
  • YAML Output Format — scoring model and verdict definitions
  • Stable interfaces — the CLI option list including --output
  • Loop convergence contract — definition of the suggestedLoopSignal values the dashboard renders