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.98.0 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.98.0 rustup toolchain install 1.98.0 --profile minimal rustup default 1.98.0 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 run: | set -euo pipefail if ! cargo run -q -p jouleclaw-brownfield --example scan_self -- --fail-on-lost; then 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 isn't consumer-visible." echo "::error::Run locally with: cargo run -p jouleclaw-brownfield --example scan_self" exit 1 fi