acorde-analysis 1.2.2

Deterministic, explainable music analysis for acorde scores
Documentation
# acorde-analysis

Deterministic, explainable analysis primitives for [`acorde-core`](https://docs.rs/acorde-core).
The crate has no I/O, renderer, or host dependencies.

The capability slice labels chord-shaped pitch collections, records adjacent melodic intervals
(including exact signed microtonal cents), does not bridge rests or pitchless events, and estimates
major/minor keys from duration-weighted diatonic pitch coverage. It also reports explicit cadence
transitions and aligned voice-leading observations, including parallel-perfect flags. Every
result contains stable `NoteAddr` evidence and a rule identifier. Voice-leading observations
retain exact signed cents for microtonal motion. SATB diagnostics classify voice crossing, wide
spacing, and parallel-perfect motion with typed severity. Key estimation returns all tied best
candidates, so relative-major/minor ambiguity is preserved instead of inventing a single key.
Repeated three-note interval motifs and explicit rest-terminated phrase boundaries are also
reported with source spans.

```rust
use acorde_analysis::analyze_score;
use acorde_core::Score;

let result = analyze_score(&Score::default());
assert!(result.chords.is_empty());
```

`analyze_batch` preserves input order for finite collections, while `analyze_stream` returns a
lazy iterator for host-side streaming. Both use the same deterministic result contract.

`AnalysisCache` provides a bounded deterministic in-memory cache keyed by the schema-versioned
score fingerprint. Editing any canonical score content produces a cache miss automatically;
eviction is insertion-order based and does not depend on hash-map iteration.
Its `analyze_batch` method preserves input order while reusing duplicate or previously cached
scores.
Editors can call `invalidate(score)` after dropping an old score snapshot to reclaim that entry
without disturbing other cached results.
`stats()` exposes deterministic hit/miss counters, and `reset_stats()` clears only those counters
for host-side measurement windows.
For an editor replacement flow, `analyze_after_edit(previous, current)` reclaims the previous
snapshot and returns the analysis for the current score in one operation.
When both snapshots have the same canonical fingerprint, the helper keeps and reuses the existing
entry instead of forcing an unnecessary recomputation.

`diff_analysis(previous, current)` reports changed result categories in stable order while keeping
score fingerprint changes separate from category changes. This lets hosts update explanations or
views selectively without treating metadata-only edits as analysis-content changes.
`analysis_provenance(result, address)` returns the deterministic rule ID, confidence, and source
evidence for every finding that contains the selected `NoteAddr`.
`explain_analysis_change(previous, current, address)` combines before/after provenance with the
deterministic category diff for an explainable editor update.

Offline benchmark consumers can use `BenchmarkCase` and `run_benchmark` with hand-verified
category counts. The report includes predicted counts, precision, recall, explanation
completeness, and category-level `BenchmarkFailure` records with missing or excess predictions.
`run_benchmark_suite` additionally aggregates case status and metrics; an empty suite is reported
as zero for each aggregate metric. Latency should be measured by the host benchmark runner and is
intentionally not embedded in the deterministic analysis result.

Applications can register deterministic extensions with `AnalysisPass` and execute them through
`run_analysis_passes`. Pass IDs are validated and sorted before execution, so results do not depend
on registration order; empty or duplicate IDs return `AnalysisPassError`.

Analysis resource-budget ownership is documented in the [security contract](../../docs/security/threat-model.md).