Incremental / Delta Audit

Delta audit — check only recently changed code for regressions and new bugs.

Sby Skills Guide Bot
TestingIntermediate
007/22/2026
Claude Code
#delta-audit#regression-testing#code-review#incremental

Recommended for


description: "Delta audit — check only recently changed code for regressions and new bugs" argument-hint: "[--working] [--commits <N>] [--range <A>..<B>] [--since <date>]"

Incremental / Delta Audit

Audit only what changed, not a whole subsystem. The goal is to catch new bugs and regressions introduced by recent work — fast — by routing each changed file to the subsystem/per-game audit dimensions that own it and applying their checks to the diff alone.

This is a meta-audit: it does not define dimensions, it dispatches to the real audit skills (each lives at .claude/commands/audit-<NAME>/SKILL.md). Use those for the authoritative checklist of any one area.

See .claude/commands/_audit-common.md for project layout, severity, methodology, deduplication, context rules, and the base finding format. See .claude/commands/_audit-severity.md for the severity scale + special rules (the NIFAL / GPU-struct / AS / SSBO rows are the ones a delta most often trips).

Step 1: Determine the diff scope

Pick the narrowest scope that covers the work under review. Default to the working tree if nothing is specified.

| Argument | Scope | Diff command | |----------|-------|--------------| | (none) / --working | uncommitted work | git diff HEAD --name-only (add --staged-less; for staged-only use git diff --staged --name-only) | | --commits <N> | last N commits | git diff "HEAD~<N>..HEAD" --name-only | | --range <A>..<B> | explicit revision range | git diff "<A>..<B>" --name-only | | --since <date> | everything since a date | base=$(git log --since="<date>" --format="%H" \| tail -1); then git diff "${base}^..HEAD" --name-only |

Then pull the actual hunks for the same scope (swap --name-only for nothing, or -U6 for more context) and the commit log:

git diff HEAD~10..HEAD --stat        # changed-file overview (substitute your scope)
git diff HEAD~10..HEAD               # the hunks you will actually audit
git log --oneline HEAD~10..HEAD      # commit themes, PR numbers, milestone tags

Audit the diff, with just enough surrounding context to confirm each finding (_audit-common.md § Methodology) — do not re-audit untouched code.

Step 2: Route each changed file to its audit dimension

Map every changed path to the audit skill(s) that own it, then apply that skill's checks to the diff. A file can hit multiple rows (e.g. a shader + its #[repr(C)] host struct → renderer and the GPU-struct-sync rule). Risk is the floor severity for an un-disproven finding in that area.

| Changed path | Owning audit(s) | Risk | |--------------|-----------------|------| | crates/renderer/src/vulkan/** (pipeline, sync, descriptors, context/) | /audit-renderer, /audit-safety, /audit-concurrency | HIGH | | crates/renderer/src/vulkan/acceleration/**, svgf.rs, gbuffer.rs, composite.rs (RT / denoise / G-buffer) | /audit-renderer | HIGH | | crates/renderer/src/vulkan/scene_buffer/**, material.rs (#[repr(C)] GPU structs) | /audit-renderer, /audit-nifal | HIGH | | crates/renderer/src/vulkan/volumetrics.rs + shaders/volumetrics_*.comp (M55) | /audit-renderer | HIGH | | crates/renderer/src/vulkan/bloom.rs + shaders/bloom_*.comp (M58) | /audit-renderer | HIGH | | crates/renderer/src/vulkan/water.rs, shaders/water.vert/water.frag, byroredux/src/systems/water.rs, byroredux/src/cell_loader/water.rs (M38) | /audit-renderer, /audit-fnv | HIGH | | crates/renderer/shaders/** (any .comp/.vert/.frag) | /audit-renderer (+ GPU-struct-sync rule) | HIGH | | crates/core/src/ecs/** | /audit-ecs, /audit-concurrency | HIGH | | crates/nif/src/blocks/**, crates/nif/src/import/**, crates/nif/src/anim/** | /audit-nif; per-game /audit-<game> | HIGH | | crates/bsa/src/** (BSA / BA2 / CSG) | /audit-nif (archive feed), per-game /audit-<game> | HIGH | | byroredux/src/material_translate.rs, crates/core/src/ecs/components/material.rs, crates/nif/src/import/collision/mod.rs (NIFAL boundary) | /audit-nifal | HIGH | | byroredux/src/env_translate.rs (EXAL boundary) | /audit-nifal (mirror), /audit-renderer | MEDIUM | | byroredux/src/ragdoll.rs, crates/physics/src/** (PHYSAL / Rapier bridge) | /audit-safety, per-game /audit-<game> | MEDIUM | | crates/spt/src/**, byroredux/src/cell_loader/refr.rs (.spt route) | /audit-speedtree | MEDIUM | | crates/plugin/src/esm/** (incl. records/misc/{water,character,world,ai,magic,effects,equipment}.rs) | per-game /audit-<game>, /audit-legacy-compat | MEDIUM | | crates/core/src/animation/** | /audit-nif (anim import), /audit-ecs | MEDIUM | | byroredux/src/cell_loader/** | per-game /audit-<game> | MEDIUM | | byroredux/src/systems/**, byroredux/src/render/** | /audit-ecs, /audit-renderer, /audit-performance | MEDIUM | | byroredux/src/scene/** | per-game /audit-<game> | MEDIUM | | byroredux/src/main.rs, byroredux/src/commands/** | /audit-ecs | MEDIUM | | crates/scripting/**, crates/pex/**, crates/papyrus/** | /audit-scripting | MEDIUM | | crates/save/** | /audit-save | MEDIUM | | byroredux/src/asset_provider/archive.rs (sibling-BSA auto-load, AE path strip) | per-game /audit-<game> | MEDIUM | | crates/audio/src/{lib,tests}.rs | /audit-audio | MEDIUM | | crates/sfmaterial/src/** (Starfield CDB) | /audit-starfield | MEDIUM | | crates/bgsm/src/** (FO4+ BGSM/BGEM) | /audit-fo4, /audit-nifal | MEDIUM | | crates/facegen/src/** | per-game /audit-<game> | MEDIUM | | crates/debug-ui/src/**, crates/renderer/src/vulkan/egui_pass.rs (egui overlay → touches draw_frame) | /audit-renderer, /audit-concurrency | MEDIUM | | **/tests/**, **/*_tests.rs, byroredux/tests/golden_frames.rs | /audit-regression | LOW | | *.md, docs/** | /audit-tech-debt (doc rot) | LOW |

Layout shifts that often surprise a delta audit: render.rs, systems.rs, scene.rs, and cell_loader.rs are all directories now (byroredux/src/render/, systems/, scene/, cell_loader/), each with a thin mod.rs dispatch + topic submodules + *_tests.rs siblings. crates/renderer/src/vulkan/acceleration/ and scene_buffer/ are likewise split. The authoritative tree is in _audit-common.md § Project Layout — route against it, not against memory.

Step 3: Regression-focused checks on each changed file

For every changed file, read the hunk + minimal context and ask:

  • [ ] New bug — logic error, off-by-one, wrong byte width, missing version/era gate (B-splines reach FNV/FO3, not just Skyrim+).
  • [ ] Contract break — did a public signature change without all call sites updating? (git grep the symbol across the workspace.)
  • [ ] Silent divergence — was a value built at two sites and only one edited? The classic NIFAL leak: the two Material load paths.
  • [ ] Unsafe delta — new unsafe block, changed safety invariant, or a safety comment that no longer matches the body (_audit-severity: MEDIUM floor for unsafe-without-comment).
  • [ ] Lock / query delta — changed RwLock scope or a new multi-component query? Verify TypeId-sorted acquisition (deadlock → HIGH floor).
  • [ ] Vulkan delta — new pipeline/barrier/sync, AS build/refit, or descriptor write? Missing barrier or wrong AS geometry → see the severity special-rules table (HIGH/CRITICAL floors).
  • [ ] GPU-struct lockstep — a touched #[repr(C)] struct (GpuInstance/GpuCamera/GpuMaterial/GpuLight) and its mirror in every shader that reads it. Size/offset drift → HIGH.
  • [ ] Missing test — a changed code path with no corresponding test update. Flag in the "Missing Tests" section even if the code is right.

Rust-specific deltas

  • [ ] Drop ordering — Vulkan destruction order still reverse of build?
  • [ ] Error handling — new unwrap()/expect() on a recoverable path?
  • [ ] Lifetimes — a borrow whose scope changed (temporary outliving / new dangling borrow)?
  • [ ] Trait impls — a new impl consistent with the existing family (Component storage decl, Send+Sync)?

NIFAL boundary delta (when the diff touches the material rows)

The canonical material contract is resolve-once at the translation boundary, no render-time fallback — so a wrong value there is silently wrong across every game.

  • Material::metalness / roughness are plain resolved f32 (crates/core/src/ecs/components/material.rs), not Option. They are finalized by Material::resolve_pbr, which clamps and — only when the upstream override arrived NaN — falls back to classify_pbr_keyword (the surviving keyword classifier; it is a sentinel-backstop for non-pre-classified sources, not a per-draw safety net).
  • NIF-imported content is pre-classified at import (classify_legacy_pbr) so resolve_pbr only clamps; BGSM/BGEM also arrive pre-classified. Confirm a diff did not leave either scalar NaN at draw time.
  • Both load paths must still route through translate_material: byroredux/src/cell_loader/spawn.rs (REFR spawn) and byroredux/src/scene/nif_loader.rs (loose-NIF). If a diff adds a field to one and not the other, that is the divergence this layer exists to prevent. Defer to /audit-nifal for the full single-boundary checklist.

NIFAL particle / collision chain (multi-file translation surfaces)

These are not single-file changes — a diff to one tier is incomplete without the others:

  • Particle emitter: typed blocks (crates/nif/src/blocks/particle.rs: NiPSysEmitter / NiPSysEmitterCtlr / NiPSysEmitterCtlrData / NiPSysGrowFadeModifier) → extraction (crates/nif/src/import/walk/mod.rs: extract_emitter_params / extract_emitter_rate) → system (byroredux/src/systems/particle.rs: apply_emitter_params). Audit all three.
  • Collision shape: a new Bhk*Shape parser (crates/nif/src/blocks/collision/) is also a translation surface — crates/nif/src/import/collision/mod.rs must map it to CollisionShape, or it is silently dropped (MEDIUM floor; HIGH if it removes visible game content). PHYSAL now consumes ragdoll constraints, so a collision diff can ripple into byroredux/src/ragdoll.rs + crates/physics/.

Step 4: Deduplicate

Run the dedup pass from _audit-common.md § Deduplication for every finding (existing-issue search + prior-report scan) before recording it. A regression of a closed issue is reported as "Regression of #NNN".

Extra Per-Finding Field

In addition to the base format in _audit-common.md:

  • Changed in: <file-path> (commit <hash> / working tree)

Output

Write to: docs/audits/AUDIT_INCREMENTAL_<TODAY>.md (YYYY-MM-DD).

Report structure

  1. Change summary — scope (range/commits/since), files changed, themes.
  2. Routing map — each changed file → dimension(s) it was audited under.
  3. Findings — new bugs + regressions (base format + Changed in).
  4. Missing tests — changed code paths with no test update.

Then suggest:

/audit-publish docs/audits/AUDIT_INCREMENTAL_<TODAY>.md
Related skills