From the adversarial review (2026-08-04). SDK/macro ergonomics found by writing probe suites; all verified against real compiler output. Ordered by expected M1 porting pain:
? does not work with anyhow::Error in case bodies: the blanket impl<E: std::error::Error> From<E> for Failure (crates/component-test-core/src/verdict.rs) excludes anyhow::Error (which deliberately doesn't implement Error), and the workaround type Failure isn't in the prelude the macro injects. Porting a conformance suite means fallible helpers everywhere; today every call site needs a hand-rolled map_err plus tribal knowledge of the import. Proposal: prelude extension trait (or_fail() on Result<T, E: Display>) and/or export Failure in the prelude; document the anyhow gap on failed().
check!(cond) without a message dies with unexpected end of macro invocation — everyone arriving from assert! writes this first. Add a ($cond:expr $(,)?) arm using stringify!($cond) as detail, or a targeted compile_error!.
- Diagnostic-anchor spans: the decline-pair lint anchors at
Span::call_site() (attribute line) instead of the first offending tagged case (spans are already collected); verdict-shape/type errors also land the primary caret on the attribute (arity is checked syntactically, types aren't) — in a 50-case file users read secondary notes to find the case. Anchor at the case fn (e.g. a typed local let __f: fn(&TestContext) -> _ = #path; spanned at the fn).
- No way to tag a whole suite: root-level
#[cases(tags(...))] dies with cannot find attribute. Support #[suite(tags(...))] or emit a targeted "put it on an inner module" error.
- Non-leaf WIT-label violations point at the case fn, not the offending module ident; and
NameError::BadChar says contains forbidden character 'A' without stating the grammar — the message isn't frozen, improve it.
- The two example suites model two diagnostic APIs: fixture-suite (the
#[suite] DX showcase) uses raw ctx.diagnostic("...".into()) while sample uses the curated ctx.diag("..."). Unify on diag (goldens pin output, not source — no regeneration needed).
Related: #28.
From the adversarial review (2026-08-04). SDK/macro ergonomics found by writing probe suites; all verified against real compiler output. Ordered by expected M1 porting pain:
?does not work withanyhow::Errorin case bodies: the blanketimpl<E: std::error::Error> From<E> for Failure(crates/component-test-core/src/verdict.rs) excludesanyhow::Error(which deliberately doesn't implementError), and the workaround typeFailureisn't in the prelude the macro injects. Porting a conformance suite means fallible helpers everywhere; today every call site needs a hand-rolledmap_errplus tribal knowledge of the import. Proposal: prelude extension trait (or_fail()onResult<T, E: Display>) and/or exportFailurein the prelude; document the anyhow gap onfailed().check!(cond)without a message dies withunexpected end of macro invocation— everyone arriving fromassert!writes this first. Add a($cond:expr $(,)?)arm usingstringify!($cond)as detail, or a targetedcompile_error!.Span::call_site()(attribute line) instead of the first offending tagged case (spans are already collected); verdict-shape/type errors also land the primary caret on the attribute (arity is checked syntactically, types aren't) — in a 50-case file users read secondary notes to find the case. Anchor at the case fn (e.g. a typed locallet __f: fn(&TestContext) -> _ = #path;spanned at the fn).#[cases(tags(...))]dies withcannot find attribute. Support#[suite(tags(...))]or emit a targeted "put it on an inner module" error.NameError::BadCharsayscontains forbidden character 'A'without stating the grammar — the message isn't frozen, improve it.#[suite]DX showcase) uses rawctx.diagnostic("...".into())while sample uses the curatedctx.diag("..."). Unify ondiag(goldens pin output, not source — no regeneration needed).Related: #28.