ID |
|
|---|---|
Status |
Backlog |
Bucket |
architecture |
Priority |
6 |
Theme |
structural-refactor |
Widen string-carrier intermediates onto Rejection (R58 follow-up)
R58 lifted the direct candidate-hint producers onto typed
Rejection.AuthorError.UnknownName
factories. Five intermediate carriers still flatten the typed shape into prose before it reaches
a Rejection consumer, blocking five candidate-hint producers from reaching the typed surface
their factories (unknownForeignKey, unknownTypeName, unknownEnumConstant,
unknownNodeIdKeyColumn, unknownColumn) already exist for. R58 Phase D shipped the factories;
this plan adds the carrier widenings so the typed values reach consumers.
R58 is currently In Review. If R58 reverts, R66 Phase A reverts with it (the producer-side factories disappear). The dependency is captured in prose rather than
depends-on:because the factories are merged on the working branch.
Carrier audit
-
BuildContext.ParsedPath.errorMessage: String; populated byparsePathElement’s `List<String> errorsaccumulator (BuildContext.java:388); surfaces at everyparsePath().hasError()consumer inFieldBuilder,BuildContext,NodeIdLeafResolver,TypeBuilder. Widening to a singlerejection: Rejectionunlocks the producer atBuildContext.java:583-584(FK SQL name miss →unknownForeignKey). -
InputFieldResolution.Unresolved.reason: String; built byBuildContext.classifyInputFieldand consumed byBuildContext(typename inference, nested field aggregation) andInputFieldResolver. Widening torejection: RejectionunlocksBuildContext.java:875-878(typename in@nodeId→unknownTypeName) andBuildContext.java:1015-1016(column-in-path-leg →unknownColumn). -
ArgumentRef.ScalarArg.UnboundArg.reason: String; single producer atFieldBuilder.java:849-852(column on filter table). Called out as out of scope in R58 Phase D. Widening torejection: Rejectionunlocks theunknownColumnmigration. -
EnumMappingResolver.EnumValidation.Mismatch(Rejection rejection); the carrier already holds aRejection, but the producer atEnumMappingResolver.java:159-162joins per-constant misses into a single prose blob viaString.join("; ", mismatches)and wraps inRejection.structural(…). Surfacing each miss asunknownEnumConstantwith its own candidate set requires widening the component toList<Rejection> rejections. The "GraphQL type is not an enum" branch at:145-146is single-rejection; under the widened shape it becomesList.of(Rejection.structural(…)). -
TypeBuilderaggregations ; same multi-miss pattern as (4):-
TypeBuilder.java:397-409(keyColumnErrors: List<String>) joins to oneRejection.structural(…). Widen the accumulator toList<Rejection>and migrate per miss tounknownNodeIdKeyColumn. -
TypeBuilder.java:686-708(failures: List<InputFieldResolution.Unresolved>) joinsUnresolved.reasonstrings. After Phase A2 eachUnresolvedcarries aRejection; this site fans those out per miss ontounknownColumn.
-
Phases
-
Phase A ; single-Rejection carrier widenings (items 1-3). Independent; each independently shippable.
-
A1 (item 1): widen
ParsedPath.errorMessage: String→rejection: Rejection. UpdateparsePathElement’s accumulator from `List<String>toList<Rejection>and join into a singleRejectionatparsePathexit (one rejection per path-parse failure preserves the current per-call-site error semantic). Migrate the FK-name producer at:583-584ontounknownForeignKey. Other producers inparsePathElementconstructRejection.structural(reason)to preserve current prose at the byte boundary. -
A2 (item 2): widen
InputFieldResolution.Unresolved.reason: String→rejection: Rejection. Migrate:875-878ontounknownTypeNameand:1015-1016ontounknownColumn. OtherUnresolvedconstruction sites wrap inRejection.structural(reason). Required before B2’s second site (TypeBuilder.java:686-708) soUnresolvedalready carries the typed shape its consumer can fan out. -
A3 (item 3): widen
ArgumentRef.ScalarArg.UnboundArg.reason: String→rejection: Rejection. Migrate:849-852ontounknownColumn. -
Phase B ; multi-miss carrier widenings (items 4-5). Decided shape:
List<Rejection>at the aggregating carrier; consumer fans out to NUnclassifiedField/UnclassifiedType(and therefore NValidationError`s).Rationale: keeps R58’s single-rejection-per-`Unclassified* /ValidationErrorinvariant intact; each typed miss surfaces as its own validator entry, which is more useful for IDE/LSP consumers than a joined prose blob. Considered alternatives: aRejection.Multi(List<Rejection>)seal arm (rejected: complicates the seal and forces everyRejectionconsumer to handle nesting); wideningValidationErroritself (rejected: R58 Phase I just locked the single-rejection shape). -
B1 (item 4): widen
EnumValidation.Mismatch(Rejection)→Mismatch(List<Rejection>). Producer at:159-162emits oneunknownEnumConstantper missing constant. Branch at:145-146becomes a singleton list. Consumer inFieldBuilder(column-bound enum filter classification) fans out to oneUnclassifiedFieldper typed rejection. -
B2 (item 5): same widening on
TypeBuilder.keyColumnErrorsand thefailures-list rendering at:686-708. Producers emit oneUnclassifiedTypeper typed miss instead of one type-levelRejection.structural(joined). Author-side UX shift: a multi-miss@nodetype now produces N validator entries (one per unresolved key column) instead of one entry with semicolon-joined prose. Documented in the changelog as an intentional trade for typed candidate hints.
Tests
-
Pipeline (extending
R58TypedRejectionPipelineTest): -
One case per Phase A unlock point (
unknownForeignKeyvia path-parse,unknownTypeNamevia@nodeId,unknownColumnvia path leg,unknownColumnvia filter-table column). -
Phase B fan-out: a 3-miss enum mapping produces 3
ValidationError`s, each carrying `Rejection.AuthorError.UnknownNamewithattemptKind = ENUM_CONSTANTand its own candidate list. -
Phase B fan-out: a 2-miss
@node(keyColumns:)produces 2ValidationError`s, each carrying `unknownNodeIdKeyColumn. -
Model (extending
RejectionRenderingTest):EnumValidation.Mismatch.message()joins the list via `; ` so the byte-stable log surface validator consumers depend on is preserved when a caller chooses to render the multi-miss carrier as a single line (e.g. log emission).
Out of scope
-
Threading nested rejection chains as a typed
Rejection.NestedRejectarm. Same deferral as R58 ; not on any in-flight consumer’s path. -
LSP fix-its consuming the typed
UnknownName.candidateslists this plan unlocks. R18 wires the consumer; this plan supplies the typed shape. -
Lifting
ArgumentRef.UnclassifiedArg.reasonontoRejection. Separate axis (argument classification, not field classification); same shape-pattern but different producer set. -
BuildWarning.message. Single producer site; premature. -
Renaming
RejectionKind. Same deferral as R58.