Skip to content

feat(errors): expose RFC-0003 structured failures - #52

Merged
HsiangNianian merged 2 commits into
devfrom
feat/rfc-0003-structured-errors
Aug 29, 2026
Merged

feat(errors): expose RFC-0003 structured failures#52
HsiangNianian merged 2 commits into
devfrom
feat/rfc-0003-structured-errors

Conversation

@HsiangNianian

@HsiangNianian HsiangNianian commented Aug 28, 2026

Copy link
Copy Markdown
Member

Summary

  • add the RFC-0003 versioned error envelope across the Rust engine
  • preserve UTF-8 byte spans, expected parse constructs, resource counters, and replay metadata
  • expose a typed Python exception hierarchy while retaining ValueError compatibility
  • add CLI JSON error output and document the implemented boundary

Typed success results remain part of the later result/API milestone; this PR keeps existing success payloads compatible.

Validation

  • cargo test --all-targets --all-features
  • cargo test --release checked_i64_
  • cargo test --release rfc_0002_
  • cargo test --release property_
  • cargo clippy --all-targets --all-features -- -D warnings -A non-local-definitions
  • uv run --frozen ruff check .
  • uv run --frozen ruff format --check .
  • uv run --frozen mypy --strict src/oneroll docs/rfcs/0004-target-api.pyi
  • uv run --no-sync python -m unittest discover -s tests -v
  • uv run --no-sync sphinx-build -W --keep-going -b html docs/source docs/_build/html

Closes #12

Summary by Sourcery

Expose stable RFC-0003 structured failures across the Rust, Python, and CLI interfaces without breaking existing ValueError-based callers.

New Features:

  • Expose RFC-0003 versioned structured failure envelopes from the Rust engine, including phases, stable codes, UTF-8 byte spans, expected constructs, resource counters, and replay metadata.
  • Provide a typed Python exception hierarchy for structured failures while preserving ValueError compatibility.
  • Add CLI JSON output for successful results and structured errors.

Bug Fixes:

  • Preserve request-scoped random metadata on failures across calculator operations.
  • Correct hexadecimal seed parsing to populate all seed bytes reliably.

Enhancements:

  • Propagate parse, validation, evaluation, and contextual error metadata consistently across the Rust and Python boundaries.

Documentation:

  • Document structured failures and CLI JSON error output in the README and user documentation.
  • Add RFC-0003 structured-error conformance coverage against the published JSON schema.

Tests:

  • Add Rust and Python tests covering stable error families, UTF-8 spans, resource counters, replay metadata, compatibility entry points, and CLI JSON failures.

Copilot AI lite review requested due to automatic review settings August 28, 2026 06:00
@sourcery-ai

sourcery-ai Bot commented Aug 28, 2026

Copy link
Copy Markdown

Reviewer's Guide

This PR implements RFC-0003 structured failure envelopes end to end: Rust errors gain stable codes and contextual metadata, Python exposes typed ValueError-compatible exceptions, the CLI supports machine-readable JSON failures, and documentation plus schema-backed tests define and validate the boundary.

Sequence diagram for structured Python error propagation

sequenceDiagram
    participant Caller
    participant OneRoll
    participant RustCore
    participant DiceParser
    participant DiceCalculator

    Caller->>OneRoll: roll(expression)
    OneRoll->>RustCore: _roll_dice(expression)
    RustCore->>DiceParser: parse_expression(expression)
    DiceParser-->>RustCore: DiceError
    RustCore-->>OneRoll: PyValueError with _oneroll_error_json
    OneRoll->>OneRoll: _raise_structured(error)
    OneRoll-->>Caller: ParseError or typed OneRollError
Loading

File-Level Changes

Change Details Files
Introduces the RFC-0003 versioned structured error model and preserves contextual metadata across engine failures.
  • Adds serializable error phases, envelopes, execution fields, UTF-8 byte spans, expected parser constructs, resource counters, and replay descriptors.
  • Wraps calculator operations with request-scoped random metadata while retaining root-cause matching for existing Rust callers.
  • Maps legacy error variants to stable phase/code pairs and exposes envelope serialization helpers.
src/errors.rs
src/calculator.rs
src/parser.rs
src/conformance_tests.rs
src/lib.rs
Adapts the Python API to raise typed structured exceptions without breaking ValueError compatibility.
  • Adds OneRollError subclasses selected from phase and code, with helpers for spans, random metadata, counters, and envelope conversion.
  • Transfers the Rust envelope through the PyErr while keeping the underlying exception type ValueError.
  • Routes all public Python entry points and policy validation through structured-error conversion.
src/oneroll/__init__.py
src/python_bindings.rs
Adds RFC-0003 JSON error handling to the CLI and updates user-facing documentation.
  • Adds --json output for successful results and structured failure envelopes with appropriate exit codes.
  • Suppresses progress output in JSON statistics mode and formats policy failures before execution.
  • Documents structured failures, compatibility behavior, and CLI usage.
src/oneroll/__main__.py
README.md
docs/source/index.rst
docs/source/language.rst
docs/source/limits.rst
docs/source/randomness.rst
Adds schema-backed coverage for structured failures across Rust, Python, and CLI boundaries.
  • Validates parse spans and expected constructs, resource counters and replay metadata, arithmetic and policy subclasses, and legacy core ValueError envelopes.
  • Exercises every compatibility entry point and verifies exact JSON-only CLI output for execution, policy, and statistics failures.
tests/test_structured_errors.py
src/errors.rs
src/conformance_tests.rs

Assessment against linked issues

Issue Objective Addressed Explanation
#12 Replace string-only Rust domain failures with the RFC-0003 versioned structured error envelope, including stable phases and codes, resource counters, replay metadata, and structured parser fields.
#12 Expose structured errors through Python while preserving ValueError compatibility, with UTF-8 byte spans, typed exception subclasses, and machine-readable payloads that validate against the RFC schema. Most Python paths are converted to structured exceptions, but several parser/domain failures still originate as generic ParseError values without an intrinsic failing span or expected construct. They receive only a whole-source fallback span in the Python binding, so the requirement that parse failures identify the failing byte range and expected construct is not satisfied consistently.
#12 Provide RFC-0003 JSON error output in the CLI and ensure failures are atomic, exposing no partial instruction, roll, or trace results.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - I've found 1 issue

Prompt for AI Agents
Please address the comments from this code review:

## Individual Comments

### Comment 1
<location path="src/calculator.rs" line_range="418-432" />
<code_context>
-        self.charge_output_items_for_rolls(&rolls)?;
-        self.charge_serialized_output(&rolls)?;
-        Ok(rolls)
+        let result = (|| {
+            self.charge_instruction_activation()?;
+            let rolls = self.roll_dice_with_budget(dice)?;
+            self.charge_output_items_for_rolls(&rolls)?;
+            self.charge_serialized_output(&rolls)?;
+            Ok(rolls)
+        })();
+        self.attach_random_context(result)
     }

</code_context>
<issue_to_address>
**issue (broader_impact):** Batch evaluation failures never include the `batch` metadata that the Python exception hierarchy parses and exposes: `evaluate_batch` attaches only random metadata, so `OneRollError.batch` is always `None` for `roll_multiple` failures and callers cannot identify the failed sample or total sample count.

**Triggers:** When a batch operation fails after evaluation has started, such as `roll_multiple("1 / 0", 2)`.

**Suggested fix:** Attach a batch descriptor containing the algorithm, seed, sample count, and failing sample index to errors returned from `evaluate_batch`, and serialize it in `ExecutionError`.
</issue_to_address>

Sourcery assessment

Approval pending. 1 finding to address first.

Blocking findings: src/calculator.rs:432


Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread src/calculator.rs
@HsiangNianian
HsiangNianian merged commit 1e574a3 into dev Aug 29, 2026
21 checks passed
@fu050409
fu050409 deleted the feat/rfc-0003-structured-errors branch August 29, 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.

Return structured parse, validation, and evaluation errors with spans

3 participants