Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
d6bf343
ci(mutants): add .cargo/mutants.toml config
utof Apr 24, 2026
24b6bbf
ci(mutants): add per-PR cargo mutants --in-diff workflow
utof Apr 24, 2026
1c9846c
test(media): extract synthesis helpers to tests/common/mod.rs
utof Apr 25, 2026
f2faa4f
test(media): cover B1 — directory path I/O failure (warn log)
utof Apr 25, 2026
4238fc3
test(media): cover B4 — garbage PNG (image_dimensions Err arm)
utof Apr 25, 2026
d3907cd
test(media): cover B5 — garbage MP4 (mp4parse Err arm)
utof Apr 25, 2026
1d2a85c
test(media): cover B8 — audio-first-then-video track ordering
utof Apr 25, 2026
cea01f2
test(media): cover B2 — corrupt TIFF (parser Err arm)
utof Apr 25, 2026
2ff26c0
test(media): cover B3 — NUL/space padded ASCII trim
utof Apr 25, 2026
5549957
test(media): cover B9 — Make+Model only, no DateTimeOriginal
utof Apr 25, 2026
c57d51f
ci(fuzz): scaffold fuzz/ leaf workspace for cargo-fuzz targets
utof Apr 25, 2026
0793766
fix(ci): restore !CLAUDE.md + !docs/superpowers/**/*.md gitignore exc…
utof Apr 25, 2026
7bcdd79
test(fuzz): add exif fuzz target wrapping ImageExtractor::extract
utof Apr 25, 2026
b963f42
test(fuzz): add blake3 fuzz target wrapping Blake3Service hash methods
utof Apr 25, 2026
697b275
ci(fuzz): add weekly-cron + manual-dispatch fuzz workflow
utof Apr 25, 2026
68c12bd
ci(bench): add criterion dev-dep + [[bench]] entry to perima-hash
utof Apr 25, 2026
a90ae6b
test(bench): add Blake3Service hash-throughput criterion benchmark
utof Apr 25, 2026
7a474fd
test(bench): add SqliteSearchRepository::search FTS5 latency benchmark
utof Apr 25, 2026
a19e76d
ci(bench): add weekly-cron + manual-dispatch criterion workflow
utof Apr 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions .cargo/mutants.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# cargo-mutants config — see https://mutants.rs/configuration
#
# WHY this file exists: per-PR mutation testing wired as a CI workflow
# (.github/workflows/mutants.yml) needs centralised scope + test-runner
# config that survives independent of the workflow file. See spec
# docs/superpowers/specs/2026-04-24-cargo-mutants-design.md.

# WHY test_tool = "nextest" (mandatory): CLAUDE.md "Test stack" bans
# `cargo test` for non-doc tests because of the SQLite lock-order
# inversion deadlock (GH #131). cargo-mutants defaults to `cargo test`;
# this override routes through `cargo nextest` instead. The repo-level
# scripts/no-cargo-test.sh enforces the ban only on yml/sh/justfile
# string scans, so cargo-mutants invoked via `cargo mutants` would NOT
# be caught — but the deadlock still hits, hence the override is
# load-bearing for correctness.
test_tool = "nextest"

# WHY exclude_globs targets crates/desktop/**: Tauri/GTK system-dep
# friction (see Batch D known env limitation in
# docs/superpowers/plans/architecture-audit-done-cheatsheet.md). Most
# desktop code is wire-up; mutation surface is low value. Re-introducing
# desktop coverage requires a separate Linux-deps setup commit + a
# justification for the CPU cost. This is the SOLE mechanism scoping
# mutation generation — do NOT also pass `--workspace --exclude
# perima-desktop` via `additional_cargo_args` (that key is for build
# flags like `--release`/`--all-features` and would clash with
# cargo-mutants' own per-package selection layer).
exclude_globs = ["crates/desktop/**"]

# WHY timeout_multiplier = 5.0: ScanUseCase + WriteCmd dispatch are
# fundamentally async + spawned tasks; cargo-mutants' default per-mutant
# timeout heuristics over-trigger on async fns. 5x default ≈ 25s per
# mutant kill attempt before cargo-mutants logs `TIMEOUT`. Tune down
# once baseline is observed across ~10 PRs.
timeout_multiplier = 5.0
80 changes: 80 additions & 0 deletions .github/workflows/bench.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# criterion benchmarks weekly cron + manual workflow_dispatch.
# Slice 4 of T1 test-architecture decomposition.
# See spec: docs/superpowers/specs/2026-04-25-criterion-benches-design.md
#
# WHY a separate workflow (not added to ci.yml): criterion runs are
# bounded MINUTES, not seconds; per-PR queue cost is disproportionate.
# Decoupling lets bench fail-or-skip independently of the main matrix.

name: bench

on:
schedule:
- cron: '0 7 * * 1' # Mondays 07:00 UTC (1h after slice 3 cargo-fuzz)
workflow_dispatch:
inputs:
sample_size:
description: 'criterion sample size per benchmark'
type: string
default: '30'

permissions:
contents: read

env:
CARGO_TERM_COLOR: always

jobs:
bench:
name: cargo bench
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2

# WHY GTK deps even though no benched crate uses them: matches
# slice 1/3 workflow-scaffolding pattern. perima-hash + perima-db
# don't transitively need GTK; the install is defensive.
- name: Install image/system deps
run: |
sudo apt-get update -q
sudo apt-get install -yq \
libgtk-3-dev \
libwebkit2gtk-4.1-dev \
libayatana-appindicator3-dev \
librsvg2-dev \
patchelf

# WHY continue-on-error: per spec D-4, observability-only mode.
# Bench step might fail on transient runner issues; swallowing
# the exit lets artifact upload still run. Consistent posture
# with slice 1/3 workflows.
#
# WHY scoped to `-p perima-hash -p perima-db` (NOT --workspace):
# this slice pins exactly 2 benched crates. `cargo bench --workspace`
# would compile + invoke benches in any future workspace crate that
# gets a [[bench]] entry, scope-creeping the slice's contract.
- name: Run cargo bench (perima-hash + perima-db)
continue-on-error: true
env:
SAMPLE_SIZE: ${{ github.event.inputs.sample_size || '30' }}
run: |
# Tee bench output to GITHUB_STEP_SUMMARY so the run page shows
# the latest numbers without artifact download.
{
echo "## criterion benchmark results"
echo ""
echo '```'
cargo bench -p perima-hash -p perima-db -- --sample-size "$SAMPLE_SIZE" 2>&1
echo '```'
} | tee -a "$GITHUB_STEP_SUMMARY"

- name: Upload criterion target dir (raw HTML reports)
if: always()
uses: actions/upload-artifact@v4
with:
name: criterion-reports
path: target/criterion/
if-no-files-found: ignore
91 changes: 91 additions & 0 deletions .github/workflows/fuzz.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
# cargo-fuzz weekly cron + manual workflow_dispatch.
# Slice 3 of T1 test-architecture decomposition.
# See spec: docs/superpowers/specs/2026-04-25-cargo-fuzz-design.md
#
# WHY a separate workflow (not added to ci.yml): fuzz runs are bounded
# minutes, NOT seconds; they would dominate per-PR queue if added to
# the main matrix. Decoupling lets fuzz fail-or-skip independently of
# the main `just ci` matrix.

name: fuzz

on:
schedule:
- cron: '0 6 * * 1' # Mondays 06:00 UTC
workflow_dispatch:
inputs:
max_total_time:
description: 'Per-target fuzz duration in seconds'
type: string
default: '300'

permissions:
contents: read

env:
CARGO_TERM_COLOR: always

jobs:
fuzz:
name: cargo fuzz / ${{ matrix.target }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
target: [exif, blake3]
steps:
- uses: actions/checkout@v4

# WHY install nightly here (not via dtolnay/rust-toolchain@stable):
# the fuzz/ subdir's rust-toolchain.toml pins nightly; rustup honors
# the toolchain file when invoked from inside fuzz/.
- name: Install nightly via fuzz/rust-toolchain.toml
run: |
cd fuzz
rustup show

- uses: Swatinem/rust-cache@v2
with:
workspaces: fuzz -> target

- name: Install cargo-fuzz
uses: taiki-e/install-action@v2
with:
tool: cargo-fuzz

# WHY install GTK deps defensively: matches slice 1 cargo-mutants
# workflow pattern to keep workflow scaffolding uniform across the
# T1 test-architecture slices. Strictly speaking, perima-hash +
# perima-media do NOT transitively require GTK on ubuntu-latest's
# default features; keeping the install avoids silent breakage if
# a future cargo-features change pulls in something system-dep-ful.
- name: Install image/system deps
run: |
sudo apt-get update -q
sudo apt-get install -yq \
libgtk-3-dev \
libwebkit2gtk-4.1-dev \
libayatana-appindicator3-dev \
librsvg2-dev \
patchelf

# WHY continue-on-error: per spec D-4, observability-only mode.
# libFuzzer exits non-zero on a crash; without continue-on-error
# the very first crash blocks all future runs of this workflow
# until the crash is fixed. Precedent: kani.yml + mutants.yml use
# the same pattern.
- name: Run cargo fuzz target ${{ matrix.target }}
continue-on-error: true
env:
MAX_TIME: ${{ github.event.inputs.max_total_time || '300' }}
run: |
cd fuzz
cargo fuzz run ${{ matrix.target }} -- -max_total_time=$MAX_TIME

- name: Upload artifacts (if any crashes)
if: always()
uses: actions/upload-artifact@v4
with:
name: fuzz-artifacts-${{ matrix.target }}
path: fuzz/artifacts/${{ matrix.target }}/
if-no-files-found: ignore
82 changes: 82 additions & 0 deletions .github/workflows/mutants.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# cargo-mutants per-PR mutation testing — Linux-only, observability-only.
# See spec: docs/superpowers/specs/2026-04-24-cargo-mutants-design.md
#
# WHY a separate workflow (not added to ci.yml's matrix): mutation
# testing is CPU-heavy + slow (minutes per file); decoupling lets it
# fail-or-skip independently of the main `just ci` matrix. PR contributors
# see two checks instead of one. Failure of mutants.yml does NOT block
# merge during slice 1 (continue-on-error swallows non-zero exit).

name: mutants
on:
pull_request:

permissions:
contents: read

env:
CARGO_TERM_COLOR: always

jobs:
mutants:
name: cargo mutants --in-diff
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- uses: actions/checkout@v4
with:
# WHY fetch-depth: 0 — `git diff origin/<base>..` requires the
# base branch in local history; depth 0 fetches the full graph.
fetch-depth: 0

- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2

# WHY taiki-e/install-action (NOT cargo-binstall): this workflow
# runs on ubuntu-latest only; the BASH_FUNC_* leak that broke
# taiki-e/install-action on windows-latest (GH #137) does NOT
# affect Linux. Multi-tool comma syntax fetches both binaries in
# one step.
- uses: taiki-e/install-action@v2
with:
tool: cargo-mutants,cargo-nextest

# WHY Tauri Linux deps even though crates/desktop is excluded from
# mutation: `cargo metadata` (which cargo-mutants invokes
# internally) resolves the WHOLE workspace including
# perima-desktop's transitive deps. Without GTK headers, metadata
# resolution fails before any mutation happens.
- name: Install Tauri Linux deps
run: |
sudo apt-get update -q
sudo apt-get install -yq \
libgtk-3-dev \
libwebkit2gtk-4.1-dev \
libayatana-appindicator3-dev \
librsvg2-dev \
patchelf

# WHY `| tee git.diff` (vs `> git.diff`): tee echoes the diff into
# the CI log so reviewers can see what cargo-mutants will operate
# on without downloading the artifact. Matches the canonical
# mutants.rs/in-diff example.
- name: Compute diff against base branch
run: git diff origin/${{ github.base_ref }}.. | tee git.diff

# WHY continue-on-error: per spec D-4, this slice is observability-
# only. cargo-mutants exits 2 when mutants survive, 3 on timeout,
# 4 on baseline failure. Without continue-on-error the very first
# PR with a survivor blocks the workflow + defeats the
# observability-first calibration goal. Precedent: kani.yml uses
# the same pattern. Drop this in a future slice once the survivor
# baseline is stable.
- name: cargo mutants --in-diff
continue-on-error: true
run: cargo mutants --no-shuffle -vV --in-diff git.diff

- name: Upload mutants.out
if: always()
uses: actions/upload-artifact@v4
with:
name: mutants-out
path: mutants.out
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@
# .github/; the blanket **/*.md rule would otherwise exclude markdown
# files added there (e.g. pull_request_template.md).
!.github/**
# WHY: fuzz/README.md is a contributor-facing quick-start doc for the
# cargo-fuzz harness; it needs to be tracked so new contributors can
# find invocation + triage instructions without reading source files.
!fuzz/README.md
target/
.DS_Store
*.swp
Expand Down
Loading
Loading