soothfast v0.1.0

Reports#

Every table, chart, badge, and changelog entry on this site is rendered from a baseline, or a pair of them, rather than typed by hand. The example below calls the real renderer directly on a small baseline of its own.

Perf tables and badges#

rust · runnable
use serde_json::json;
use soothfast_report::{badges, perf_table};

fn main() {
    let baseline = json!({ "items": { "demo::checksum": {
        "perfcnt": { "instructions": 812 },
        "walltime": { "median_ns": 41.2, "p99_ns": 58.0 },
        "alloc": { "allocs": 0, "bytes": 0 }
    }}});

    print!("{}", perf_table::markdown(&baseline));
    println!("{}", badges::coverage_badge("docs", 92));
    println!("{}", badges::gate_badge(Some(true)));
}
recorded outputcargo soothfast docs capture
| item | instructions | median | p99 | allocs | polls |
|---|---:|---:|---:|---:|---:|
| `demo::checksum` | 812 | 41.2ns | 58.0ns | 0 | n/a |
{"color":"brightgreen","label":"docs","message":"92%","schemaVersion":1}
{"color":"brightgreen","label":"soothfast gate","message":"passing","schemaVersion":1}

perf_table::rows is the shared data extraction step. markdown and html both call it and are otherwise identical. badges::badge is the raw shields.io endpoint shape ({schemaVersion, label, message, color}), and coverage_badge and gate_badge are the two callers that pick color for you: green at 90% or above, yellow at 70% or above, red below that, and passing, failing or unknown for a gate verdict.

cargo soothfast report render#

console
$ cargo soothfast report render -p mylib --baseline self
report: wrote docs/perf/summary.md
report: wrote docs/perf/summary.html
report: wrote docs/perf/badges/gate.json
report: wrote docs/perf/llms.txt

summary.md and summary.html are perf_table::markdown and html over the baseline. badges/gate.json reads .soothfast/gate-status.json, written by the last gate run, through badges::gate_badge.

The trend SVGs (trend-instructions.svg, trend-walltime_median_ns.svg, trend-allocs.svg) only appear once .soothfast/trend.jsonl holds at least two points for that metric. Below that trend_chart::render returns None and report render skips the file rather than draw an empty axis.

llms.txt is written only when -p PKG is given. It is PKG's public surface, meaning each signature and its first doc line, joined against the baseline by id and falling back to covers=. An agent reading it can answer "what does X measure right now" without guessing from a README that may be out of date.

Trend and changelog#

cargo soothfast trend append -p mylib appends one line per run ({unix, commit, noise_pct, items}) to .soothfast/trend.jsonl. That file feeds both the SVG charts above and trend render, which prints each metric's drift from first to last point straight to stdout. It writes no file of its own, since charting is report render's job.

--from-baseline NAME builds the point from a saved baseline instead of measuring again — a pipeline whose gate already ran with --save-baseline appends the very numbers it just verified. -p PKG scopes the point the same way it scopes a measured run: other packages' entries and buildcost pseudo-items in the shared baseline file stay out.

cargo soothfast report changelog -p mylib --against-ref v1.0 merges a fresh "Unreleased" section into CHANGELOG.md in place. Every ## [released] section already there survives. Re-running it is idempotent: it replaces the Unreleased section rather than appending a duplicate.

The draft leads with what merged since v1.0, grouped into Features, Fixes, Performance, Documentation, Dependencies and Internal by each commit's type, each entry carrying the pull request its subject named. That comes from git log, not a forge API, since every merge lands as a squash whose subject already holds the number. Release commits, and the bots that regenerate derived artifacts, are dropped so a release does not list its own paperwork.

The section icons come from a shipped default. Override any subset in soothfast.toml; a name that is not a section is an error rather than a silent default:

toml
[changelog.icons]
features = "📐"
performance = "⏱"

Below a rule sit the derived sections, evidence rather than narrative: the public API diff against v1.0, and the measured movement past gate thresholds. A section with nothing to report is omitted rather than shipped holding a sentence saying so, and a draft with nothing at all to report is its heading alone.

Hand-written prose survives the same regeneration by living inside a <!-- soothfast:notes --> / <!-- /soothfast:notes --> pair anywhere in the Unreleased section. Add it once; every later run carries it forward, spliced back in right after the heading, until the section is renamed to a real version and frozen like the rest of the released history.

A section with no notes yet gets that pair with commented-out prompts for the two things no measurement supplies: what the release means for someone using it, and what a consumer has to do. They stay comments until someone writes into them, so an untouched block renders as nothing.

Pass several -p PKG flags to cover more than one crate, and leave --against-ref off entirely for a first release, where there is no earlier version to diff against and the draft lists the surface it ships instead.

built with cargo soothfast docs build source