Skip to content

refactor(errors): migrate eleven crates from thiserror to snafu - #111

Draft
forkwright wants to merge 4 commits into
mainfrom
fix/86-thiserror-to-snafu
Draft

refactor(errors): migrate eleven crates from thiserror to snafu#111
forkwright wants to merge 4 commits into
mainfrom
fix/86-thiserror-to-snafu

Conversation

@forkwright

Copy link
Copy Markdown
Owner

Refs #86

Draft deliberately: this has never been compiled. 51 files, +1428/-750, salvaged from a crashed
session where the work was uncommitted on disk when the orchestrating machine died mid-migration. It
is committed to preserve it, not because it is ready. CI is the first thing that will ever have
looked at it.

Scope, stated honestly

Migrated to snafu — 11 crates: cache, core, decode, embed, encoders, hipcore,
kernels, loader, praxis, taxis, transformers. Eleven manifests now declare snafu.

Not migrated, with the reason for each:

Site Why it is still thiserror
crates/tokenize/src/error.rs A real library error enum. The one genuine remainder.
crates/embed/benches/stella_throughput.rs Bench target, not library surface
crates/logismos/tests/phase_2_pipeline.rs, phase_3_debug.rs, phase_3_fixture_check.rs, phase_3_stella_parity.rs Test targets, not library surface

The standard's prohibition is on libraries. Whether it should also bind bench and test targets is
a judgement call rather than an oversight, and it should be made deliberately rather than inherited
from where a crashed session happened to stop.

#86 therefore stays open, scoped down to tokenize plus that decision.

Two traps this migration carries

Recorded here because whoever finishes it will hit both, and neither is obvious:

  • snafu context selectors are generic over Into<FieldType>. A bare integer literal passed to a
    usize field infers as i32 and fails to compile with the trait bound 'usize: From<i32>' is not satisfied. Suffix the literal (0usize); do not widen the field to make the error go away —
    the field type is the correct one.
  • #[snafu(transparent)] only fires where the ? is present. A missing ? silently skips the
    conversion, so the error surfaces as the wrong variant rather than as a compile failure.

What a reviewer should expect

This is the one PR in this salvage set where "does it compile" is a genuinely open question. If CI
red-flags it, the failures are most likely the first trap above, concentrated at context-selector
call sites. That is ordinary migration fallout rather than a sign the approach is wrong.

Worth checking beyond compilation: every error variant should still be reachable and still carry
the same information. A migration that compiles while quietly collapsing two variants into one, or
dropping a context field, is the failure mode that survives a green build.

Verification status

None. No gate, no review, no compile. There is currently no build box in the fleet — the
rented one lapsed — so no Gate-Passed trailer is obtainable by anyone. crates/hipcore/build.rs
also fails hard without ROCm, which no box here has, so a local workspace build is impossible
independently of that. CI is the verifier of record.

forkwright added 2 commits August 17, 2026 08:38
Salvaged from a crashed session: this was uncommitted on disk when the
orchestrating machine died mid-migration. It has had no gate, no review, and no
compile -- committing it preserves the work; CI is the first witness.

Migrated: cache, core, decode, embed, encoders, hipcore, kernels, loader,
praxis, taxis, transformers. Eleven manifests now declare snafu.

NOT migrated, and the reason each is left:
- crates/tokenize/src/error.rs -- a real library error enum, still thiserror.
  This is the one genuine remainder.
- crates/embed/benches/stella_throughput.rs and four crates/logismos/tests/*.rs
  -- bench and test targets, not library surface. The standard's prohibition is
  on libraries; these are a separate judgement call rather than an oversight.

#86 therefore stays OPEN, scoped down to tokenize plus a decision on the
bench/test sites.

Two traps this migration carries, recorded for whoever finishes it: snafu
context selectors are generic over Into<FieldType>, so a bare integer literal
passed to a usize field infers as i32 and fails to compile -- suffix the literal
rather than widening the field. And a #[snafu(transparent)] conversion only
fires where the `?` is present, so a missing `?` silently skips it.
…uppressed

The snafu migration left `Error` imported with no code reference, which is an
error under -D warnings. Removing it compiles and silently breaks every
`# Errors` doc link in both modules -- rustdoc resolves `[`Error::Variant`]`
only against items in scope, and all twenty references here are doc links.

Split from its import group so the expectation covers this one import: a
genuinely-unused import added to the group later still fails the gate. If
`Error` ever gains a code reference the expectation goes UNFULFILLED and
errors, which retires the suppression rather than letting it go stale.

Refs #86
@forkwright

Copy link
Copy Markdown
Owner Author

Staying a draft, with an accurate account of what it needs. This branch was salvaged from a session that died mid-work and was never compiled — so its state was unknown, not merely unverified.

What the CI cycles established

The first blocker was two unused import: Error errors in taxis. The obvious fix — delete the import — would have compiled and silently broken every # Errors doc link in both modules: all twenty Error references there are intra-doc links, which rustdoc resolves only against items in scope. Fixed instead by splitting the import and declaring why it exists, so a genuinely-unused import added to that group later still fails the gate, and if Error ever gains a code reference the expectation goes UNFULFILLED and errors rather than going stale.

Clearing that revealed what it was hiding. 22 errors across four cratestaxis 2, cache 1, kernels 4, loader 15 — in two classes:

  • 11 × E0277: the trait bound 'u64: From<i32>' is not satisfied
  • 2 × E0283: type annotations needed

Both are type-inference consequences of the migration rather than typos. snafu's generated context selectors change inference at error-construction sites, so numeric literals that previously resolved as u64 through thiserror's #[from] now resolve as i32. Each site needs an explicit annotation or cast, and the count behind these is still unknown because the compiler aborts at the first failing crate.

Why it is not being finished in this pass

This repo cannot be built off a ROCm host. crates/hipcore/build.rs hard-fails without hip/hip_runtime_api.h, so cargo check on metis dies before reaching any of the affected crates — verified, and it is the same constraint logismos#14 records. Every iteration therefore costs a full CI cycle, and because the compiler aborts per crate, each cycle reveals only the next layer rather than the whole set.

That makes this a real unit of work needing a build environment that can see the whole error surface at once, not a small fix. Calling it nearly-done because the first error was trivial would repeat exactly the mistake the first error concealed.

What it needs

  • A ROCm-capable environment, or a --keep-going build that reports every crate's errors in one pass instead of stopping at the first. Without that, the error count is a lower bound and every estimate from it is guesswork.
  • One pass over the E0277/E0283 sites, annotating or casting at each error-construction point.
  • A check that the migration preserved error semantics, not only that it compiles: the same conditions must still produce the same variants. Eleven crates changed error type; compiling proves the shapes line up, not that the behaviour did.

The work is worth doing — snafu is the fleet standard and this covers eleven crates — but it should be picked up deliberately rather than pushed through CI one revealed layer at a time.

forkwright added 2 commits August 19, 2026 09:20
…gration (literal suffixes + annotations)

Refs #86

The salvaged migration branch had never compiled; CI's first look
aborted at 22 errors across 4 crates. Every failure was the migration
fallout PR #111's body documents, plus its -D warnings shadow:

- snafu context selectors are generic over Into<FieldType>: bare
  integer literals inferred as i32 (E0277 u64: From<i32>) and bare
  "...".into() message fields left the selector's type parameter
  unresolvable (E0283). Literals suffixed (0u64); literal .into()
  message fields converted to .to_string(), matching the surrounding
  format!/.to_string() idiom.
- Imports the migration left unused under -D warnings: Error kept only
  for intra-doc links is split out with #[expect(unused_imports)] (the
  taxis idiom), and test modules now import Error directly instead of
  through 'use super::*' so the expectation stays fulfilled in
  lib-test builds. kernels' LaunchSnafu is cfg-gated on
  not(logismos_no_gpu_kernels) -- only the GPU launcher body
  constructs it. cache/flat.rs drops Error outright (no doc links).
- loader::gguf::Reader::byte_range_for gains
  #[expect(clippy::too_many_lines)] (105/80): six eager context
  constructions plus the documented overflow rationale push it past
  the default; splitting the validation chain is a refactor beyond
  this fix.

Error census: loader 20 (15 lib incl. 11x E0277 + 2x E0283 + 2 unused
imports, 5 lib-test of the same literal/annotation shape), kernels 4,
taxis lib-test 2, cache 1, encoders 3, praxis 6 -- all the documented
shape. No error variants collapsed: every construction still carries
the same fields it did pre-migration; 268 workspace tests pass.

Known remainder, deliberately NOT fixed here: crates/embed never had
its Cargo.toml flipped to snafu (23 of its 28 errors cascade from the
missing dependency), and embed/src/stella.rs constructs
core::EmbeddingError in pre-migration shapes at 5 sites. That is a
different fix shape than the literal pattern, so it is reported rather
than improvised; embed and its only dependent (the logismos facade)
still fail to compile.

Verified locally against a CI-equivalent HIP header set (Ubuntu noble
ROCm 5.7.1 packages + libclang, no GPU):
  cargo check   --workspace --all-targets --exclude embed --exclude logismos
  cargo clippy  --workspace --all-targets --exclude embed --exclude logismos -- -D warnings
  cargo test    --workspace --exclude embed --exclude logismos   (268 passed, 0 failed)
  cargo test    --release -p kernels
  cargo fmt     --all -- --check
@forkwright

Copy link
Copy Markdown
Owner Author

Compile-error sweep pushed as e973e5a (merge 99aed10 + fix commit). PR left draft — un-drafting/merge is the author's call.

Fixed: 35 errors across 6 crates (loader 20, kernels 4, taxis 2, cache 1, encoders 2, praxis 6), all the documented literal/annotation shape or its -D warnings shadow. Local: 268 passed / 0 failed over 53 suites (--exclude embed --exclude logismos), release kernels leg green, clippy -D warnings clean, fmt clean — via a CI-equivalent harness (Ubuntu ROCm 5.7.1 headers + libclang).

Remaining, deliberately not improvised: embed never got migrated. Its Cargo.toml still declares thiserror (23 of 28 CI errors cascade from the missing dep), and 5 call sites + 1 test assertion in embed/src/stella.rs (lines 298, 306, 310, 341, 356, 697) construct core::EmbeddingError in pre-migration shapes. The logismos facade is its only dependent. The fix is forced — manifest flip + core's pub selectors, payloads 1:1 — but outside the literal-suffix mandate, so it was stopped on rather than guessed at.

CI on the push: deny/audit/check-trailer/ai-attribution pass; full-gate-build fails only at embed (lib 28 + lib-test 29), confirming the six fixed crates compile in CI.

Minor: the fix commit body says "encoders 3", actual is 2 — worth correcting at squash time.

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.

1 participant