Skip to content

feat(core): typed error domains — Tell/Ask split, PanicError, ActorStopReason (#113)#135

Merged
joeldsouzax merged 2 commits into
mainfrom
core-error-113
Jul 5, 2026
Merged

feat(core): typed error domains — Tell/Ask split, PanicError, ActorStopReason (#113)#135
joeldsouzax merged 2 commits into
mainfrom
core-error-113

Conversation

@joeldsouzax

Copy link
Copy Markdown
Contributor

Rebuilds src/error.rs from the kameo reference oracle (card #113, epic #122), diverging where the type system pays for itself.

What changed

  • TellError<M> — fire-and-forget delivery failures (ActorNotAlive / MailboxFull); the undelivered message is always handed back (msg is total).
  • AskError<M, E>composes TellError via Deliver(..), plus the three reply-side failures a tell can never have (Timeout / Interrupted / Handler(E)). From<TellError> bridges them so ? works. Whether the message returns is encoded in the variant, not an Option<M>.
  • Retryability is a method (is_retryable / is_terminal), never a caller's guess (rule feat: replace DHT registry with key-expr discovery #3): only delivery backpressure is retryable — a Timeout is not (the message is already in the actor), and a Handler domain error (nexus Conflict) must never be re-driven as backpressure.
  • ActorStopReason (Normal/Killed/Panicked/SupervisorRestart + is_normal) and PanicError, which holds the type-erased payload behind a plain Arc<dyn ReplyError> — no Mutex (the Send+Sync bound recovers Sync without a lock). PanicReason distinguishes a lifecycle-hook failure from a handler panic (the supervisor's restart-storm signal).
  • downcast-rs + thiserror adopted (rule feat: replace DHT registry with key-expr discovery #3: no manual Display).

Divergence from the card's finalized note

The card listed the two-type Tell/Ask split as "considered, not adopted"; it was adopted here (leverage the type system, push erasure to the edges). Timeout is classified not retryable (the card's original test list had it retryable). See the card comment for the full rationale.

Deferred to producing cards

ActorStopReason::LinkDied/PeerDisconnected (#120/#121, needs ActorId), PanicReason::OnLinkDied (#120), TellError::SendTimeout (#118), serde on the remote tier, and the actor_not_alive_unifies_terminal sequence test (needs a spawnable actor, #116/#117).

Verification

  • nix flake check green (14 checks)
  • 13 tests (TDD red→green) incl. the @bug conflict_is_domain_not_retryable probe and Display stability
  • Mutation (cargo-mutants): 0 survivors
  • Coverage baseline updated; README unchanged (core is not public API yet)

Closes #113. Absorbs #50 (thiserror), #53 (downcast-rs).

…opReason (#113)

Rebuild src/error.rs from the kameo reference oracle, diverging where the
type system pays for itself. Split kameo's single SendError into two honest
types:

- TellError<M>: fire-and-forget delivery failures (ActorNotAlive / MailboxFull);
  the undelivered message is always handed back (msg is total).
- AskError<M, E>: composes TellError via Deliver(..), plus the three reply-side
  failures a tell can never have (Timeout / Interrupted / Handler(E)).
  From<TellError> bridges them so `?` works. Whether the message returns is
  encoded in the variant, not an Option<M>.

Retryability is a method (is_retryable / is_terminal), never a caller's guess
(rule #3): only delivery backpressure is retryable — a Timeout is not (the
message is already in the actor), and a Handler domain error (where nexus's
Conflict lives) must never be re-driven as backpressure.

Lifecycle side: ActorStopReason (Normal / Killed / Panicked / SupervisorRestart
+ is_normal) and PanicError, which holds the type-erased payload behind a plain
Arc<dyn ReplyError> — no Mutex, since the ReplyError Send+Sync bound recovers
Sync without a lock. PanicReason distinguishes a lifecycle-hook failure from a
handler panic (the supervisor's restart-storm signal). downcast-rs + thiserror
adopted (no manual Display).

Deferred to producing cards: ActorStopReason::LinkDied / PeerDisconnected,
PanicReason::OnLinkDied, TellError::SendTimeout, serde on the remote tier.

13 tests (TDD red→green) incl. the @bug conflict_is_domain_not_retryable probe
and Display stability. Mutation: 0 survivors. nix flake check green.
# Conflicts:
#	docs/testing/coverage-baseline.md
@joeldsouzax joeldsouzax merged commit fb4a27f into main Jul 5, 2026
6 checks passed
@joeldsouzax joeldsouzax deleted the core-error-113 branch July 5, 2026 18:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

core(error): rebuild from kameo reference — typed error domains

1 participant