Problem
The core model was designed for RFC 9457 — ShortMessage is documented as the problem title, DetailedMessage as detail (Error.cs XML docs), and the generated catalog mints urn:problem:{service}:{code} problem types (--service-name is mandatory precisely to render them). Yet no runtime code maps an Error to a ProblemDetails shape anywhere; the URN builder (ProblemType.cs) lives only in the net8.0 GenDoc tooling, unreachable from a deployed netstandard2.0 app. Every adopter hand-writes the one mapping where Design Principle 3 (public/internal separation) can be silently violated, and the catalog's documented HTTP examples can drift freely from actual API responses — the exact drift the thesis exists to eliminate, relocated to the HTTP boundary. The comparison page concedes this axis to ErrorOr.
Impact
The single highest-leverage competitive gap; the first thing API-team evaluators bounce off.
Direction
- Core (dependency-free, netstandard2.0):
error.ToProblemDetails(options) returning a neutral model — title = ShortMessage, detail = DetailedMessage (explicit opt-in), extensions code/instanceId/occurredAt, type composed by the same URN builder as the catalog (extract ProblemType from GenDoc into the core or a shared source file so runtime and catalog provably agree). DiagnosticMessage unreachable by construction.
- Satellite
FirstClassErrors.AspNetCore (net8.0+, following the RequestBinder/Testing/Dummies packaging precedent): Outcome<T> → IResult/IActionResult terminals, an IExceptionHandler for DiagnosableException, a taxonomy-driven default status policy (DomainError/PrimaryPortError → 4xx; SecondaryPortError by Transience → 502/503/500) with per-code overrides, and services.AddFirstClassErrors(serviceName, …) bridging RequestBinderOptions.Default at composition time.
- Fold in the binder host helpers the code already anticipates (
ArgumentSourceExtensions.cs:7-9): a System.Text.Json-naming-policy-aware IArgumentNameProvider and HttpRequest extraction helpers.
Acceptance criteria
- A consumer can return a catalog-consistent RFC 9457 response from an
Outcome failure in one call, without touching DiagnosticMessage.
- The problem
type URN is byte-identical between runtime responses and the generated catalog for the same code.
- The core package gains no runtime dependency.
Context
Surfaced by the 2026-07-20 architecture, design & ecosystem audit (doc/handwritten/for-maintainers/audit/2026-07-20-firstclasserrors-architecture-and-design-audit.md, §13.2/§14-C5; branch claude/firstclasserrors-audit-83gap7).
Problem
The core model was designed for RFC 9457 —
ShortMessageis documented as the problemtitle,DetailedMessageasdetail(Error.cs XML docs), and the generated catalog mintsurn:problem:{service}:{code}problem types (--service-nameis mandatory precisely to render them). Yet no runtime code maps anErrorto a ProblemDetails shape anywhere; the URN builder (ProblemType.cs) lives only in the net8.0 GenDoc tooling, unreachable from a deployed netstandard2.0 app. Every adopter hand-writes the one mapping where Design Principle 3 (public/internal separation) can be silently violated, and the catalog's documented HTTP examples can drift freely from actual API responses — the exact drift the thesis exists to eliminate, relocated to the HTTP boundary. The comparison page concedes this axis to ErrorOr.Impact
The single highest-leverage competitive gap; the first thing API-team evaluators bounce off.
Direction
error.ToProblemDetails(options)returning a neutral model —title= ShortMessage,detail= DetailedMessage (explicit opt-in), extensionscode/instanceId/occurredAt,typecomposed by the same URN builder as the catalog (extractProblemTypefrom GenDoc into the core or a shared source file so runtime and catalog provably agree).DiagnosticMessageunreachable by construction.FirstClassErrors.AspNetCore(net8.0+, following the RequestBinder/Testing/Dummies packaging precedent):Outcome<T>→IResult/IActionResultterminals, anIExceptionHandlerforDiagnosableException, a taxonomy-driven default status policy (DomainError/PrimaryPortError → 4xx; SecondaryPortError byTransience→ 502/503/500) with per-code overrides, andservices.AddFirstClassErrors(serviceName, …)bridgingRequestBinderOptions.Defaultat composition time.ArgumentSourceExtensions.cs:7-9): a System.Text.Json-naming-policy-awareIArgumentNameProviderandHttpRequestextraction helpers.Acceptance criteria
Outcomefailure in one call, without touchingDiagnosticMessage.typeURN is byte-identical between runtime responses and the generated catalog for the same code.Context
Surfaced by the 2026-07-20 architecture, design & ecosystem audit (
doc/handwritten/for-maintainers/audit/2026-07-20-firstclasserrors-architecture-and-design-audit.md, §13.2/§14-C5; branchclaude/firstclasserrors-audit-83gap7).