api-debug-lab 0.4.0

Reproducible API troubleshooting fixtures and a Rust diagnostic CLI.
Documentation
# ADR-0003: Static `&[&dyn Rule]` slice, not `Vec<Box<dyn Rule>>`

- **Status**: Accepted
- **Date**: 2026-05-10
- **Supersedes**: the original v0.1.0 → v0.2.0 implementation, which
  used `Vec<Box<dyn Rule>>`.

## Context

The rule registry is the answer to "what rules does the orchestrator
run?" — surfaced via `pub fn all_rules() -> ?` in `src/rules.rs`.

Two possible shapes:

1. `Vec<Box<dyn Rule>>` — heap-allocated, returns owned trait
   objects. Allows runtime extension; trivial to add closures or
   parameterised rules.
2. `&'static [&'static dyn Rule]` — pointer into a static array of
   references to zero-sized rule structs. No allocation. Compile-time
   fixed.

## Decision

Static slice. The eight rule structs (`AuthMissing`,
`BadJsonPayload`, …) are zero-sized — the rule's behaviour lives in
its `evaluate` impl, not in field state. Pointing references at
zero-sized statics is the same dispatch with no allocation.

```rust
static RULES: &[&dyn Rule] = &[
    &AuthMissing, &BadJsonPayload, &RateLimited, …
];
pub fn all_rules() -> &'static [&'static dyn Rule] { RULES }
```

## Consequences

**Positive.**

- `all_rules()` is essentially free; `diagnose` no longer pays a
  per-call heap allocation for the registry.
- The rule list is visible in source as a single static — easy to
  audit, easy to spot a missing registration.
- Compile-time fixed registry composes cleanly with `criterion`
  benches and the latency-budget test.

**Negative.**

- Cannot register a rule at runtime. A future caller who wanted to
  add a closure-based rule for testing would either need to fall
  back to a parallel registry or add a new public function.
- Adding a rule requires both a struct definition AND a
  registration line. The compiler does not flag a missing
  registration. This is the same trade-off as the previous
  implementation; clear over implicit.

**Neutral.**

- The `Rule` trait still requires `Send + Sync` so a future
  parallel orchestrator can iterate over the slice from multiple
  threads. The current sequential orchestrator runs rules at
  microsecond latency and does not need parallelism.