open-standards/.github/workflows/jouleclaw-public-surface-documented.yml
Transaction Science 381392897c jouleclaw public-surface audit: the report goes to a file and the log keeps the verdict
The scan writes an 800 KB report at once. On 2026-11-07 three runs of it
(main at 208ffd374, #659 and #676), on three runners, kept a log that
stopped at 256 KiB mid-report, and each job was failed twelve minutes
later, though the scan's summary read "public surface only: 0". The
summary stays in the log, and on a failure so does the report's list of
what is missing; a pass logs 393 bytes. The error lines are single-quoted,
so their backticks are no longer run as commands.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-07 11:02:35 -05:00

101 lines
4.6 KiB
YAML

name: jouleclaw public surface must stay documented
# Regression-prevention gate for jouleclaw-rs. The brownfield
# onramp (`cargo run -p jouleclaw-brownfield --example
# scan_self`) walks every crate, infers reasons for every
# public function (docs / name pattern / error-handling
# signal), and reports those whose best-available reason is
# Lost. After the 219 → 0 curation pass that closed the
# initial baseline, every new `pub fn` is expected to ship
# with the same minimal `///` doc, OR be downgraded to
# `pub(crate)` if it isn't part of the consumer-visible
# surface. The `--fail-on-lost` flag exits non-zero when any
# public-surface Lost reason remains, so this workflow is a
# permanent safety net against drift.
# One run per branch at a time. A push supersedes the run before it.
#
# There is one `macos` runner and it is the dev Mac, so every macOS job in this
# repo queues on it. With no concurrency group, a session pushing four versions
# of a branch queued four complete sets of runs and every one of them had to
# drain: on 2026-10-05 a PR's 21 contexts settled none in 70 minutes behind a
# backlog of 25 runs, and the queue grew faster than it cleared. Only the newest
# commit's verdict is wanted, so the older runs are cancelled.
#
# Verified on this forge (Forgejo 16.0.5) in `ci-test` before being added here:
# the key parses, and a newer push moves an older run to `cancelled` even while
# it is still queued, which is the case that matters.
#
# `cancel-in-progress: true` also applies to `main`. That is deliberate and
# safe here: main advances by merge commits whose tree contains the commits
# before them, so the newer run covers the older one's changes.
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
on:
# Manual re-run, so a transient runner/setup failure (which surfaces as a red
# "audit" even though the audit itself never ran) can be retried without a
# code change.
workflow_dispatch:
pull_request:
paths:
- 'jouleclaw/jouleclaw-rs/**'
- '.github/workflows/jouleclaw-public-surface-documented.yml'
push:
branches:
- main
paths:
- 'jouleclaw/jouleclaw-rs/**'
- '.github/workflows/jouleclaw-public-surface-documented.yml'
jobs:
scan:
name: Public-surface audit (jouleclaw-rs)
runs-on: linux
steps:
- name: Checkout
uses: actions/checkout@v4
# Bootstrap rustup: the docker runner image ships no Rust, and
# dtolnay/rust-toolchain cannot help — it calls `rustup`, which is absent.
- name: Install Rust 1.99.1
run: |
export PATH="$HOME/.cargo/bin:$PATH"
command -v rustup >/dev/null 2>&1 || \
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --profile minimal --default-toolchain 1.99.1
rustup toolchain install 1.99.1 --profile minimal
rustup default 1.99.1
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
- name: Cache cargo registry + target
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
jouleclaw/jouleclaw-rs/target
key: ${{ runner.os }}-cargo-brownfield-${{ hashFiles('jouleclaw/jouleclaw-rs/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-brownfield-
- name: Run brownfield self-scan with --fail-on-lost
working-directory: jouleclaw/jouleclaw-rs
# The report (stdout) goes to a file, not the log: it is about 800 KB
# written at once, and on 2026-11-07 three runs of it, on three
# runners, kept a log that stopped at 256 KiB, mid-report, and the job
# was failed twelve minutes later, though the scan's own summary read
# "public surface only: 0". The summary (stderr) stays in the log, and
# on a failure so does the report's list of what is missing, which
# comes before its module purposes.
run: |
set -euo pipefail
report="$(mktemp)"
if ! cargo run -q -p jouleclaw-brownfield --example scan_self -- --fail-on-lost > "$report"; then
sed -n '/^## Module purposes/q;p' "$report"
echo '::error::Public-surface Lost > 0 — jouleclaw-rs has undocumented public functions.'
echo '::error::Fix by adding a `///` doc comment above each flagged `pub fn`, OR by downgrading to `pub(crate)` if it is not consumer-visible.'
echo '::error::Run locally with: cargo run -p jouleclaw-brownfield --example scan_self'
exit 1
fi
echo "public-surface report: $(wc -c < "$report") bytes, not printed; run the scan locally to read it"