feat(binder)!: allow overriding structural error codes and messages - #186
Merged
Conversation
The binder's two structural errors (REQUEST_ARGUMENT_REQUIRED / REQUEST_ARGUMENT_INVALID) had hardcoded English public messages with no override or localization hook, breaking the library's own i18n story at the primary-adapter boundary. Introduce BinderErrorDefinition — a code bundled with its public-message builder (Func<string, BindingMessage>), immutable, derivable with WithCode / WithMessage — and a BindingMessage carrier (short + optional detailed). Carry one definition per structural error on RequestBinderOptions, so a code and its message are configured as a single coherent unit and can never drift apart. The message builder runs at error emission, so it may read the ambient culture and return a message localized per request. Only the two public messages are localizable; the diagnostic message stays English by convention. Options that do not override a definition keep the shipped default (code and messages), and the default definitions are exposed for symbolic branching. BREAKING CHANGE: RequestBinderOptions no longer takes argumentRequiredCode / argumentInvalidCode (ErrorCode); it takes argumentRequired / argumentInvalid (BinderErrorDefinition). The ArgumentRequiredCode / ArgumentInvalidCode properties are replaced by ArgumentRequired / ArgumentInvalid. The RequestBindingError.DefaultArgumentRequiredCode / DefaultArgumentInvalidCode accessors remain for symbolic branching. Acceptable at v0.1.0-preview.1 with no external consumers. Refs: #149 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013sD2irTr5PBVpxHt2dkRzQ
Add the <returns> tag to BinderErrorDefinition.WithCode / WithMessage, for consistency with GetMessage and the sibling PublicMessageStage.WithPublicMessage. Refs: #149 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013sD2irTr5PBVpxHt2dkRzQ
The invalid factory has its own message-wiring path; exercise it end-to-end — override the invalid message (keeping the default code) and a full code+message definition — and assert the raised REQUEST_ARGUMENT_INVALID carries the custom text while still wrapping the converter's cause. Refs: #149 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013sD2irTr5PBVpxHt2dkRzQ
Record the decision to configure each structural error as a code bundled with a per-emission message builder, superseding the code-only override of ADR-0016 and making the public messages localizable per request. Drafted Proposed (EN + FR) and indexed; ADR-0016's status is left for the maintainer to decide on acceptance. Refs: #149 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013sD2irTr5PBVpxHt2dkRzQ
Per the maintainer's decision: ADR-0018 (bundle the binder's structural error code and messages) is Accepted; ADR-0016 (the code-only override it replaces) is marked Superseded, linked to its successor. Also correct ADR-0018's French version, whose section headers and metadata labels had been left in English instead of following the established French ADR convention (Statut/Décideurs/Contexte/Décision/Justification/...). Refs: #149 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013sD2irTr5PBVpxHt2dkRzQ
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The request binder's two structural errors (
REQUEST_ARGUMENT_REQUIRED/REQUEST_ARGUMENT_INVALID) had hardcoded English public messages with no override or localization hook (#149). Each structural error is now aBinderErrorDefinition— a code bundled with a message builder evaluated at emission — configured onRequestBinderOptions. This replaces the code-only override shipped by ADR-0016 (#147): a code and its message are one coherent unit, never independently overridable, so a consumer can't strand a code from a message that no longer matches it.Type of change
Changes
BindingMessage(short + optional detailed public message carrier) andBinderErrorDefinition(code + message builder, immutable,WithCode/WithMessage).RequestBindingErrorexposesDefaultArgumentRequired/DefaultArgumentInvaliddefinitions (default codes, English messages);DefaultArgumentRequiredCode/DefaultArgumentInvalidCoderemain for symbolic branching.RequestBinderOptionsnow carriesArgumentRequired/ArgumentInvalid(BinderErrorDefinition), replacing the flatArgumentRequiredCode/ArgumentInvalidCode(ErrorCode) properties ADR-0016 shipped.RequestBinder,NestedFailure, the complex-property converters) to the new definitions; rebased ontomainafter PR Binder: factor out the duplicated missing / element-loop / wrapping logic across converters #150's converter refactor and resolved the resulting conflict against the new sharedConvertEachElementprimitive.Testing
dotnet build FirstClassErrors.slndotnet test FirstClassErrors.slnFirstClassErrors.Analyzers.UnitTests)All green: 0 warnings/errors under the solution's total
TreatWarningsAsErrorsratchet; 1014 tests passed, 0 failed, across all 8 test projects.Documentation
doc/updateddoc/handwritten/for-users/README.fr.md) updated if user-facing behavior changeddoc/handwritten/for-users/RequestBinder.en.mdand its French counterpartRequestBinder.fr.mdwere updated together (the structural-errors section and the app-default example). The top-levelREADME.fr.mddoesn't describe binder internals, so it wasn't touched.Architecture decisions
ADR-0018 was drafted as
Proposed, then set toAccepted(and ADR-0016 toSuperseded, cross-linked) at @Reefact's explicit direction during this session — not flipped unilaterally by the agent.Related issues
Closes #149
Generated by Claude Code