claudeloop-domain-modellisted
Install: claude install-skill the-vibey-project/vibey
# claudeloop domain model
> **Codex skill mirror** of `.claude/skills/claudeloop-domain-model/SKILL.md`. When this guidance changes, update Claude skill, Cursor rule, and `.agents/skills/` in the same PR.
Everything in `src/claudeloop/domain/` is a frozen dataclass or a closed
union of them, has zero third-party imports, and requires 100% test
coverage. This skill is the map; for full prose explanation with rationale
see `docs/architecture/domain-model.md`.
## `capacity.py` — CapacityState
```python
CapacityState = Available | WindowExhausted | CreditsExhausted | AuthenticationFailed
```
**The single most important fact in this codebase**: `CreditsExhausted` has
**no `resets_at` field at all** — not `None`, the type literally doesn't
carry one — because waiting for a clock can never fix an empty credits
balance. `WindowExhausted` carries `resets_at: datetime | None`.
`is_waitable(state)` is `False` only for `AuthenticationFailed`.
**Never conflate these two states** and never add a `resets_at` field to
`CreditsExhausted` "for consistency" — that would silently reintroduce the
exact bug (`claudeloop` replacing sleeping-an-hour-forever with something
smarter) this project exists to fix.
## `classify.py` — TurnSignals → CapacityState
`classify(signals: TurnSignals) -> CapacityState`. Reads **three
independent SDK signals** (a `RateLimitEvent`, `ResultMessage
.api_error_status`, `AssistantMessage.error`) — never trust
`RateLimitEvent` alone; it's reportedly dropped on so