Skip to content

Make fault: a first-class argument to raise#537

Merged
GoodForOneFare merged 1 commit into
mainfrom
gordo-faults
Jun 10, 2026
Merged

Make fault: a first-class argument to raise#537
GoodForOneFare merged 1 commit into
mainfrom
gordo-faults

Conversation

@GoodForOneFare

@GoodForOneFare GoodForOneFare commented Jun 10, 2026

Copy link
Copy Markdown
Member

Adds a fault: keyword argument to CLI::Kit.raise, letting callers attribute an exception to a fault category at the raise site:

CLI::Kit.raise(SomeError, "failed", fault: Exception::Fault::USER)

Introduces an Exception::Fault hierarchy (System, ImplicitlySystem, Project, User, Terroir) plus an Exception#fault / #fault! / #with_fault protocol. bug? now derives from the fault (Fault::System === fault) instead of being hardcoded true, so an unattributed exception still defaults to bug? == true via ImplicitlySystem (a System subclass). This keeps existing behavior fully backward compatible.

Ruby stdlib exceptions that the runtime raises itself, and so have no raise site to annotate (Interrupt, SignalException, Errno::ENOSPC, Errno::EPIPE, Errno::EIO), get a class-level default fault of Terroir. The ErrorHandler Interrupt/ENOSPC paths now pass fault: Exception::Fault::TERROIR instead of bug: false, which is equivalent (Terroir is not a System, so bug? is false).


Also dropping 3.1 support to make endless methods viable.

Add a `fault:` keyword argument to `Dev::Error#initialize` and
`CLI::Kit.raise`, allowing callers to attribute exceptions to a fault
category at the raise site:

    raise Dev::IncorrectUsage.new("bad input", fault: Fault::USER)
    CLI::Kit.raise(SomeError, "failed", fault: Fault::SYSTEM)

The `fault:` kwarg on Dev::Error is optional at runtime (defaults to
nil, falling back to the subclass's `def fault` default) so that
raising can never itself fail — better to raise with a default fault
than to raise an ArgumentError while trying to raise the real error.

The RequireExplicitRaise cop is updated to enforce fault attribution:

- `raise Klass, "msg"` is now banned (Sorbet's Kernel#raise intrinsic
  cannot be extended with fault:). The cop suggests using
  `raise MyError.new(..., fault: ...)` for Dev::Error subclasses, or
  `CLI::Kit.raise ..., fault: ...` for non-Dev::Error subclasses.

- `raise Klass.new("msg")` without fault: is flagged.

- `CLI::Kit.raise(Klass, "msg")` without fault: is flagged.

- `raise RuntimeError.new(...)` is flagged (use a specific class).

- `raise Klass.new(fault: ...)` without a message is flagged.

The cop is rewritten to use RuboCop node patterns throughout.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@GoodForOneFare GoodForOneFare merged commit aa1b514 into main Jun 10, 2026
17 checks passed
@GoodForOneFare GoodForOneFare deleted the gordo-faults branch June 10, 2026 15:52
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.

3 participants