changes
All checks were successful
CI / ci (push) Successful in 19m38s

This commit is contained in:
2026-08-26 20:55:30 +02:00
parent a557c183e9
commit 23d052278a
523 changed files with 24448 additions and 2005 deletions

View File

@@ -215,20 +215,15 @@ Constants are only for true invariants that never change. Tunable values belong
## Comments
Zero comments unless explaining **why** something non-obvious is done.
No comments. Not even to explain why — a comment is noise that drifts out of date while the code moves on.
```rust
// YES — explains a non-obvious constraint
// BLAKE3 hash, not SHA-256, because iroh uses BLAKE3 for content addressing
let hash = blake3::hash(&audio_data);
When something non-obvious needs saying, put it somewhere that cannot rot silently:
// NO — restates what the code does
// Create a new track with the given metadata
let track = Track::new(title, artist_id, duration, hash);
- **A name.** Rename the function, the variable, or the type until the reason is visible in the code.
- **A test.** A constraint worth a comment is worth a test that fails when someone breaks it. `media_is_resolved_before_the_cascade_runs` outlives any note explaining why the order matters.
- **An ADR.** Architectural reasoning belongs in `docs/adr/`, where it is versioned and discoverable.
// NO — references the task/ticket
// Added for thesis requirement §3.2
```
This applies to doc comments too.
## Dependencies
@@ -240,24 +235,19 @@ let track = Track::new(title, artist_id, duration, hash);
- Adapters: free to pull in platform crates (`axum`, `sqlx`, `iroh`, `nats`, etc.)
- Bootstrap: wiring only
## Generics Over Trait Objects
## Trait Objects for Ports
Use generics (static dispatch) for ports, not `dyn Trait`:
Ports are `dyn` behind `Arc`. Static dispatch is not worth the ergonomic cost here — use cases hold their dependencies as trait objects:
```rust
// YES — zero-cost, monomorphized
pub struct StreamTrackUseCase<T: P2pTransportPort, R: TrackRepositoryPort> {
transport: T,
repository: R,
}
// NO — heap allocation, dynamic dispatch overhead
pub struct StreamTrackUseCase {
transport: Box<dyn P2pTransportPort>,
repository: Box<dyn TrackRepositoryPort>,
pub struct Deps {
pub entries: Arc<dyn MoodEntryCommandPort>,
pub events: Arc<dyn EventPublisherPort>,
}
```
Prefer a closed enum over `dyn` only where exhaustiveness is the point — when adding a variant must force every consumer to handle it (`MetricKind`, `CorrelationStrategy`). That is a decision about compiler-enforced coverage, not about dispatch cost.
## Testing
### Coverage Targets
@@ -283,6 +273,16 @@ crates/domain/
└── track_test.rs
```
### Fakes Must Not Be Kinder Than Production
A fake that is more forgiving than the real adapter turns a test into a lie: it passes whichever way the code is written, which is worse than having no test. Two rules follow.
**A fake must reproduce the constraints the database enforces.** SQLite removes dimension rows through `ON DELETE CASCADE`, so `InMemoryStore`'s cascade clears the dimension stores registered with it via `cascades_to`. Without that, the ordering requirement in `delete_entries_by_date_range` — resolve media *before* the cascade, or every blob is orphaned — could not be tested at all.
**A fake must be able to fail.** Every port whose failure is meant to degrade rather than propagate needs a fake that refuses: `FakeMediaStorage::refusing_to_delete`, `FakeNowPlaying::failing`, `FakeRecordingLookup::failing`, `FakeWeatherLookup::failing`, `RefusingRejectionTrace`, `RefusingApiTokenStore`. A best-effort path with no failing test is a path that has never run.
When a claim cannot be observed through the fake, move the test to where the real thing runs rather than asserting it against the fake's own behaviour.
### Test Naming
Tests read as specifications: