Skip to content

test: generate the twins and one ProbeContext with the shapes (WS2) - #59

Merged
CaffeinatedCoder merged 2 commits into
release/2.2.0from
test/ws2-twins-contexts
Oct 17, 2026
Merged

CaffeinatedCoder merged 2 commits into
release/2.2.0from
test/ws2-twins-contexts

Conversation

@CaffeinatedCoder

Copy link
Copy Markdown
Owner

WS2, the twins and the contexts (concept section 6.3): each value object's twin and the one source-generated context are generated with the shapes, the three workarounds of the gate report's quirk 3 are gone, and the source-generated-context pairs they made uncoverable are covered. Every source-generated-context pair a claim wants is now credited: 2929 of 2929, up from 2642 (+287; 281 the concept's "about 270" uncoverable pairs, 6 newly in the universe). All 45 probes pass replay in all five families; nothing that passed before is lost except 4 rows that now run the configuration they name and end in a cited framework refusal (below).

What changed

1. The twin, generated with the shape (shapes.cs, Positions.cs, Sides.cs).

  • shapes.cs writes each value object's twin beside it: the wrapped type at the value object's nullability. A class value object over a struct faces the struct made nullable (int? for a sealed record class over int); a struct value object over a struct, and any value object over a class, faces the wrapped type itself. Entry.Plain is that twin. The twins' polymorphic stand-ins (PBP_int_N holding int?) are generated too, and every stand-in is declared outside the nullable context, as the containers are, so json.RespectNullableAnnotations reads no annotation one side has and the other lacks.
  • The closed set gets an explicit twin, a string (its member's name), and stays out of parity where the claims already say so: C21 excludes every closed-set pair citing a-closed-set-is-a-string-enum, L01 cites a-closed-set-key-is-its-name. No new not-promised entry.
  • The equivalence precondition is checked once: Sides.Twins(position, containing, entry, plain, vo) builds both sides of every two-sided comparison (JSON parity, C10, C01, the text claims' JSON halves, the http payload) and runs Equivalence.Shapes, which now compares nullability as well (both admit null or neither; a struct value object over a class, which no plain type mirrors, and a dictionary key or a Patch<T>'s value, never null on either side, are the exceptions). A mismatch throws InputsNotEquivalent, a harness error; the host-start catches let it through rather than reading it as "host did not start".
  • Hx.PlainNull and the http hosts' plainnull endpoints are gone: the twin carries the nullability, so the schema comparison is like with like at every endpoint, not only at bind and the root schema.

2. One ProbeContext, generated from the shapes (shapes.cs, JsonRow.cs, JsonFamily.cs).

  • It lists every value object, every struct value object made nullable (what http.nullability=nullable holds), and every twin, at the root and at each shape.position in each shape.containing, plus the Option and Result carriers L06's rows hold under the context (--l06): 8974 types.
  • json.TypeInfoResolver=source-generated-context is now strict: ProbeContext alone, no reflection. A shape the context does not list is a harness error (JsonRow.Listed, JCtx.Apply), never a fallback.
  • The reflection fallback is json.TypeInfoResolverChain=context-then-default, the existing axis value: the context, then DefaultJsonTypeInfoResolver. No new axis value. Measured on the way: System.Text.Json refuses only the resolver's own instance inserted into its chain ("Cannot insert the values of 'TypeInfoResolver' or 'TypeInfoResolverChain' to 'TypeInfoResolverChain'"); a context followed by another resolver is accepted. JsonRow inserted ProbeContext.Default a second time, which is what failed. So the pair (source-generated-context, context-then-default) is the context with the fallback, and the six exclusions that read the refusal as "STJ refuses any chain once a context is the resolver" are removed from C04, C06, C07, C14, C15 and C21 (their reason was not what STJ does; 6 pairs back in the universe, all now covered).
  • Removed, the three workarounds: JsonRow.ListedInContext's +reflection-fallback substitution; the per-claim contexts C01JsonContext, C10JsonContext, L06JsonContext (json-family.cs mode contexts becomes stand-ins, L06's polymorphic stand-ins only); SchemaClaim.StrictSourceGen's Combine(ProbeContext, SchemaJson) and its removal of the fallback from the evidence (the hosts append SchemaJson, the application's own types, to the chain, as an application does; C08's alike host now does too, for ProblemDetails); FaultClaim.StrictContext's substitution of the payload to property-of-class.
  • NoteKeys keeps every member: ctx-fallback and schema:compared-with-nullable-wrapped are marked "not called since".

3. Rows (prober rows --probe, then a replay of each).

  • Regenerated with prober rows --probe for the 20 probes whose claims take json.TypeInfoResolver, each file written whole, then replayed. Kept where the regenerated file covers as many pairs: C04, C06, C08, C12, C13, C16, L04. Elsewhere it covered fewer (C01 −14, C03 −1, C05 −1, C07 −3, C10 −22, C14 −13, C15 −4, C21 −20, L01 −35, L02 −179, L03 −29, L06 −11), so the old rows are kept there, as test: the build family's probes run one check per obligation #54 did for C11. BaseProbe's regeneration was 4 rows (C07BlanketMappingProbe already credits C07's pairs), so both C07 probes keep theirs.
  • Top-up: C21 only, one row appended (rows --claim row 31: source-generated-context with context-then-default, a record struct over bool, list-element, OpenAPI 3.0) for the one pair the old rows left, the context with the fallback. rows --claim C21 printed 301 rows for C21's 358 scattered uncovered pairs; the rest are not this step's.
  • The old rows already covered almost every formerly uncoverable pair once the substitution was gone: the rows had always held the context at every position and containing type; the harness substituted them.
  • L06's rows are its old ones, so Shapes.g.cs' carriers are what they were listed from.

4. The guard. GeneratedHarnessTests' table: Shapes.g.cs takes --l06 rows/L06OffTheWireProbe.json.gz, JsonFamilyShapes.g.cs takes stand-ins. Proven by seeding: [JsonSerializable(typeof(Cn_class_P<V_record_struct_inT>)… in the committed Shapes.g.cs fails The_committed_file_is_its_generators_output(file: "Shapes.g.cs") with "first difference at line 717"; restored, 13 of 13 pass. Still 12 generated files.

Pairs per claim (the claims that take json.TypeInfoResolver)

prober status --json, current/pairs. "Old rows, new harness" is a replay of this harness on the old rows; "regenerated rows" one on every file rows --probe wrote; "after" the committed state.

Claim before old rows, new harness regenerated rows rows kept after
C01-decimal-spelling 4344/4344 4344/4344 4330/4344 old 4344/4344
C03-composed-faults 15583/15607 15605/15607 15604/15607 old 15605/15607
C04-without-faults 15409/15426 15427/15427 15427/15427 regenerated 15427/15427
C05-with-rule 15750/15783 15781/15783 15780/15783 old 15781/15783
C06-fault-kinds 15231/15248 15249/15249 15249/15249 regenerated 15249/15249
C07-fault-responses-blanket 15946/15956 15957/15957 15954/15957 old (both probes) 15957/15957
C08-same-name-warning 15609/15609 15609/15609 15609/15609 regenerated 15609/15609
C10-preserve-guard 6797/6797 6797/6797 6775/6797 old 6797/6797
C12-described-converter 14903/14904 14904/14904 14904/14904 regenerated 14904/14904
C13-patch-required 14731/14731 14731/14731 14731/14731 regenerated 14731/14731
C14-declared-problem 14886/14903 14904/14904 14891/14904 old 14904/14904
C15-mvc-fault-status-doc 15238/15255 15256/15256 15252/15256 old 15256/15256
C16-options-agreement 14904/14904 14904/14904 14904/14904 regenerated 14904/14904
C21-openapi-schema-parity 13798/14218 13861/14219 13841/14219 old + 1 top-up row 13862/14219
L01-json-bytes 6456/6478 6473/6478 6438/6478 old 6473/6478
L02-no-quote 8215/8314 8240/8314 8061/8314 old 8240/8314
L03-one-rule 7057/7084 7079/7084 7050/7084 old 7079/7084
L04-round-trip 8290/8314 8312/8314 8312/8314 regenerated 8312/8314
L06-off-the-wire 8021/8022 8021/8022 8010/8022 old 8021/8022

The pairs with json.TypeInfoResolver=source-generated-context

Counted from the evidence (a passing check: the row's applied settings with the check's own over them) against each claim's universe less its exclusions. C21's two own-for-* converter pairs are declared inapplicable by every C21 obligation (not wanted), so every wanted pair is covered. Newly covered: 287 = 281 that the base universe held and no row could earn (the concept's estimate: about 270) + 6 the removed exclusions put back.

Claim pairs with source-generated-context credited (state-base) credited (state)
C01-decimal-spelling 93 93 93
C03-composed-faults 177 155 177
C04-without-faults 176 158 176
C05-with-rule 178 147 178
C06-fault-kinds 175 157 175
C07-fault-responses-blanket 179 168 179
C08-same-name-warning 177 177 177
C10-preserve-guard 118 118 118
C12-described-converter 173 172 173
C13-patch-required 172 172 172
C14-declared-problem 173 155 173
C15-mvc-fault-status-doc 175 157 175
C16-options-agreement 173 173 173
C21-openapi-schema-parity 169 103 167
L01-json-bytes 115 98 115
L02-no-quote 130 105 130
L03-one-rule 120 98 120
L04-round-trip 130 108 130
L06-off-the-wire 128 128 128
all 2931 2642 2929

Before and after, per probe

replay --all per family from an empty state, before on release/2.2.0 at e01932b, after on this branch. http, json and text, the families whose claims take the resolver (C02 and C11 ride in the text family):

Probe rows before checks before rows after checks after rows passing, before → after regained (category before) lost (category after)
BaseProbe expected-refusal 126, inapplicable 111, inconclusive 43, pass 131 expected-refusal 182, inapplicable 390, inconclusive 63, pass 981 expected-refusal 123, inapplicable 109, inconclusive 43, pass 136 expected-refusal 180, inapplicable 388, inconclusive 63, pass 986 333 → 332 0 1 (expected-refusal 1)
C01DecimalSpellingProbe expected-refusal 5, inconclusive 15, pass 97 expected-refusal 25, inconclusive 32, pass 528 expected-refusal 5, inconclusive 15, pass 97 expected-refusal 25, inconclusive 32, pass 528 111 → 111 0 0
C02OptionalTextProbe pass 46 pass 52 pass 46 pass 52 46 → 46 0 0
C03ComposedProbe pass 648 pass 2919 pass 648 pass 2919 648 → 648 0 0
C04WithoutFaultsProbe expected-refusal 2, pass 394 expected-refusal 2, pass 835 expected-refusal 4, pass 293 expected-refusal 5, pass 623 396 → 297 rows regenerated rows regenerated
C05RuleProbe pass 645 pass 1666 pass 645 pass 1666 645 → 645 0 0
C06FaultKindsProbe expected-refusal 4, pass 404 expected-refusal 4, pass 1628 expected-refusal 6, pass 300 expected-refusal 6, pass 1218 408 → 306 rows regenerated rows regenerated
C07BlanketMappingProbe expected-refusal 1, pass 410 expected-refusal 1, pass 474 expected-refusal 1, pass 410 expected-refusal 1, pass 474 410 → 410 0 0
C08SameNameWarningProbe pass 318 pass 545 pass 302 pass 540 318 → 302 rows regenerated rows regenerated
C10PreserveGuardProbe expected-refusal 101, inapplicable 3, inconclusive 2, pass 200 expected-refusal 113, inapplicable 3, inconclusive 2, pass 494 expected-refusal 100, inapplicable 1, inconclusive 2, pass 203 expected-refusal 112, inapplicable 1, inconclusive 2, pass 497 294 → 294 0 0
C11ParseShapesBuildProbe inapplicable 4, pass 243 inapplicable 4, pass 243 inapplicable 4, pass 243 inapplicable 4, pass 243 243 → 243 0 0
C12DescribedConverterProbe pass 332 pass 789 pass 301 pass 701 332 → 301 rows regenerated rows regenerated
C13PatchRequiredProbe inconclusive 55, pass 301 inconclusive 55, pass 657 inconclusive 51, pass 245 inconclusive 51, pass 541 356 → 296 rows regenerated rows regenerated
C14DeclaredProblemProbe expected-refusal 3, pass 418 expected-refusal 12, pass 1385 expected-refusal 4, pass 417 expected-refusal 15, pass 1382 418 → 417 0 1 (expected-refusal 1)
C15MvcFaultStatusDocProbe expected-refusal 7, pass 398 expected-refusal 8, pass 543 expected-refusal 9, pass 396 expected-refusal 11, pass 540 398 → 396 0 2 (expected-refusal 2)
C16OptionsAgreementProbe pass 310 pass 420 pass 296 pass 414 310 → 296 rows regenerated rows regenerated
C21OpenapiSchemaParityProbe inapplicable 3, inconclusive 7, pass 283 inapplicable 6, inconclusive 13, pass 475 inapplicable 4, inconclusive 6, pass 284 inapplicable 7, inconclusive 12, pass 478 283 → 284 0 0
L01JsonBytesProbe expected-refusal 187, inapplicable 120, inconclusive 21, pass 253 expected-refusal 203, inapplicable 136, inconclusive 22, pass 801 expected-refusal 186, inapplicable 112, inconclusive 21, pass 262 expected-refusal 202, inapplicable 128, inconclusive 22, pass 810 548 → 548 0 0
L02NoQuoteProbe inapplicable 218, inconclusive 132, pass 236 inapplicable 302, inconclusive 159, pass 843 inapplicable 218, inconclusive 132, pass 236 inapplicable 302, inconclusive 159, pass 843 434 → 434 0 0
L03OneRuleProbe expected-refusal 56, inapplicable 79, inconclusive 21, pass 430 expected-refusal 75, inapplicable 95, inconclusive 22, pass 1330 expected-refusal 56, inapplicable 79, inconclusive 21, pass 430 expected-refusal 75, inapplicable 95, inconclusive 22, pass 1330 561 → 561 0 0
L04RoundTripProbe expected-refusal 46, inapplicable 133, inconclusive 127, pass 280 expected-refusal 56, inapplicable 222, inconclusive 137, pass 1989 expected-refusal 18, inapplicable 74, inconclusive 59, pass 139 expected-refusal 21, inapplicable 111, inconclusive 64, pass 994 586 → 290 rows regenerated rows regenerated
L06OffTheWireProbe expected-refusal 21, inapplicable 3, inconclusive 29, pass 272 expected-refusal 22, inapplicable 15, inconclusive 38, pass 575 expected-refusal 21, inapplicable 3, inconclusive 29, pass 272 expected-refusal 22, inapplicable 15, inconclusive 38, pass 575 303 → 303 0 0

"Rows passing" counts rows with a passing check and no failing one; for the seven probes whose rows were regenerated, row numbers no longer correspond, so their regained and lost rows are not counted row by row (their pairs are in the table above: none lost).

Rows regained: none counted row by row, but checks moved: BaseProbe's rows ending pass went from 131 to 136, C21's inconclusive rows that had no twin at the value object's nullability under the context (an enum, Instant) compare now. Lost: 4 rows, each now running the configuration it names, ending in a cited framework refusal:

  • BaseProbe #119 (mvc bind form, refused value): before, the row ran on the reflection fallback (+reflection-fallback substituted) and MVC answered 400. Strict now, MVC cannot write its ValidationProblemDetails and answers 500, as it does for the twin without TypeKit: framework-refuses against fact:KB-041.
  • C14 #0, C15 build: rows per probe, deduplicated, and a watched list that stales a footprint #39 and #151 (json.MaxDepth=3): before, the payload ran at a property of a class (FaultClaim's substitution) or on the fallback, and ASP.NET alone refused the document with the same message. At the row's own position, deeper, TypeKit's transformer is refused by the schema exporter's depth check and ASP.NET alone by the writer's: record:0099 (System.Text.Json refuses the problem schema under MaxDepth=3 for plain ASP.NET too). Their pairs stay covered by other rows.

The build and EF families: a shared file moved (Row.cs, Positions.cs, Shapes.g.cs compile into the same Samples assembly), so both were replayed: 7 and 16 probes pass, rows and checks identical to before, row for row:

Probe rows before checks before rows after checks after rows passing, before → after regained (category before) lost (category after)
C09BaseRecordEqualityProbe pass 101 pass 202 pass 101 pass 202 101 → 101 0 0
C17Cmtk0014Probe pass 283 pass 512 pass 283 pass 512 283 → 283 0 0
C18Cmtk1023Probe pass 280 pass 840 pass 280 pass 840 280 → 280 0 0
C19Cmtk0010KindsProbe pass 282 pass 508 pass 282 pass 508 282 → 282 0 0
C20ShouldHoldProbe pass 261 pass 727 pass 261 pass 727 261 → 261 0 0
C22GeneratorRefusalsProbe pass 144 pass 432 pass 144 pass 432 144 → 144 0 0
E01ColumnParityProbe pass 175 pass 307 pass 175 pass 307 175 → 175 0 0
E02ValueTranslatesProbe inapplicable 12, pass 203 inapplicable 24, pass 406 inapplicable 12, pass 203 inapplicable 24, pass 406 203 → 203 0 0
E03ColumnRoundTripProbe pass 177 pass 177 pass 177 pass 177 177 → 177 0 0
E04IntegerKeyProbe pass 28 pass 28 pass 28 pass 28 28 → 28 0 0
E05OwnCompareToProbe pass 90 pass 90 pass 90 pass 90 90 → 90 0 0
E06OwnConverterNoValueProbe pass 12 pass 12 pass 12 pass 12 12 → 12 0 0
E07ClosedSetNameProbe pass 9 pass 11 pass 9 pass 11 9 → 9 0 0
E08OptionResultNotColumnsProbe pass 20 pass 40 pass 20 pass 40 20 → 20 0 0
E09OrNoneInDbProbe pass 18 pass 18 pass 18 pass 18 18 → 18 0 0
E10ReadBackSkipsValidationProbe pass 30 pass 60 pass 30 pass 60 30 → 30 0 0
E11UseTypeKitOrderProbe pass 14 pass 14 pass 14 pass 14 14 → 14 0 0
E12ConventionByTypeProbe pass 12 pass 12 pass 12 pass 12 12 → 12 0 0
E13PrimitiveCollectionProbe pass 10 pass 10 pass 10 pass 10 10 → 10 0 0
E14InheritanceKeyProbe pass 9 pass 9 pass 9 pass 9 9 → 9 0 0
E15CompositeKeyProbe pass 8 pass 8 pass 8 pass 8 8 → 8 0 0
E16OwnConfigurationWinsProbe pass 25 pass 25 pass 25 pass 25 25 → 25 0 0
L05EqualityProbe pass 309 pass 793 pass 309 pass 793 309 → 309 0 0

New violations: none in the committed state. Every one met on the way was the harness's, fixed here: context gaps (V? under http.nullability=nullable, a root decimal? whose metadata asks the options for decimal, L06's nullable carriers listed through the contract build), the stand-ins' nullable annotations, three C21 schema comparisons that lost their normalisation or citation with plainnull (a nullable struct value object against a class twin; a twin record class bound by its properties; OpenAPI 2.0 writing the twin's nullable enum property as {"allOf":[{"x-nullable":true}]}, cited docs:src/CodoMetis.TypeKit.AspNetCore/README.md#what-the-document-says, KB-026 seen from the twin), and FaultClaim's exact-message compare (above).

Build time

The Samples' rebuild after a harness change (build-verdict build test/CodoMetis.TypeKit.Samples in the replay's worktree, Probes/Row.cs touched, three runs each, Linux x64, nothing else running): 38–39 s before, 73–75 s after (+35 s), under the minute, so one context, not split per family. A trial context with every Option and Result carrier at every position (23266 types) took 3 min 49 s, which is why L06's carriers come from its rows. A second partial declaration of ProbeContext is no way to split it: System.Text.Json's generator fails on it ("The hintName 'ProbeContext.Boolean.g.cs' … must be unique").

Seeds

prober seeds --probe P for each of the 22 probes of the http, json and text families, one at a time: 88 of 88 seeds caught, each by a check of its own obligation, none missed.

Seed Outcome
BaseProbe (C07-fault-responses-blanket) caught, 55 of 411 rows violate; e.g. #10 claim: violation:minimal bind query: validated value object bound the refused value (status 200) 99
C03ComposedProbe/each-error-own-set (C03-composed-faults) caught, 487 of 648 rows violate; e.g. #0 each-error-own-set: violation:/m/2/declared e=4: type https://tools.ietf.org/html/rfc9110#section-15
C03ComposedProbe/detail-and-rule-intact (C03-composed-faults) caught, 163 of 648 rows violate; e.g. #2 detail-and-rule-intact: violation:/c/2/group e=3: detail , expected the occurrence's
C03ComposedProbe/statuses-ascending (C03-composed-faults) caught, 182 of 648 rows violate; e.g. #1 statuses-ascending: violation:/rg/m/3/wo: document lists 503,412,410,409,404,403,400, expected 400,4
C03ComposedProbe/shared-status-types (C03-composed-faults) caught, 182 of 648 rows violate; e.g. #1 shared-status-types: violation:/rg/m/3/wo 404: description "Not Found. Problem types: https://p.exa
C03ComposedProbe/unmapped-set-stops-start (C03-composed-faults) caught, 648 of 648 rows violate; e.g. #0 unmapped-set-stops-start: violation:host started with GFault unmapped (arity 2, variant declar
C04WithoutFaultsProbe/doc-omits-left-out (C04-without-faults) caught, 78 of 297 rows violate; e.g. #1 doc-omits-left-out: violation:/c/fx/ct/wo/a/decl: document lists 400,403,404,409,412,422,429,507,
C04WithoutFaultsProbe/reporting-left-out-throws (C04-without-faults) caught, 238 of 297 rows violate; e.g. #0 reporting-left-out-throws: violation:/px/c/fx/ep/wo/dup reporting HFault.Down: left out o
C04WithoutFaultsProbe/narrows-wider-status (C04-without-faults) caught, 93 of 297 rows violate; e.g. #0 narrows-wider-status: violation:host did not start: InvalidOperationException: [FaultStatus<HFa
C04WithoutFaultsProbe/left-out-everywhere-stops-start (C04-without-faults) caught, 59 of 297 rows violate; e.g. #11 left-out-everywhere-stops-start: violation:host started, though a status every endp
C05RuleProbe/rule-qualified (C05-with-rule) caught, 129 of 645 rows violate; e.g. #4 rule-qualified: violation:/px/c/3/plain e=5: rule X, expected RuleSet.X
C05RuleProbe/nested-chain (C05-with-rule) caught, 125 of 645 rows violate; e.g. #3 nested-chain: violation:/c/3/plain e=6: rule NestedRule.N, expected Holder.NestedRule.N
C05RuleProbe/stays-the-errors (C05-with-rule) caught, 376 of 645 rows violate; e.g. #2 stays-the-errors: violation:/rg/m/3/declared e=4: status 400, expected 409: {"$id":"1","type":"https://p.example
C05RuleProbe/unnamed-value-throws (C05-with-rule) caught, 269 of 645 rows violate; e.g. #0 unnamed-value-throws: violation:WithRule(flags combination) did not throw
C05RuleProbe/no-namespace (C05-with-rule) caught, 247 of 645 rows violate; e.g. #2 no-namespace: violation:/rg/m/3/declared e=4: rule Probes.PRule.A, expected PRule.A
C06FaultKindsProbe/kind-gives-status (C06-fault-kinds) caught, 131 of 306 rows violate; e.g. #3 kind-gives-status: violation:/c/k/dup reporting HFault.Garbled: status 422, expected 400: {"type":"http
C06FaultKindsProbe/description-as-detail (C06-fault-kinds) caught, 260 of 306 rows violate; e.g. #1 description-as-detail: violation:/rg/m/c6/k/one reporting HFault.Missing: detail (none), expected M
C06FaultKindsProbe/no-description-no-detail (C06-fault-kinds) caught, 306 of 306 rows violate; e.g. #0 no-description-no-detail: violation:/px/c/k/one reporting HFault.Down: detail Down, expected non
C06FaultKindsProbe/member-must-have-kind (C06-fault-kinds) caught, 306 of 306 rows violate; e.g. #0 member-must-have-kind: violation:AddFaultResponses() took FxNoKind.Blank, a member of no
C07BlanketMappingProbe/by-kind (C07-fault-responses-blanket) caught, 40 of 411 rows violate; e.g. #22 by-kind: violation:/m/c7/k/one reporting HFault.Clash: status 400, expected 409: {"type":"https:/
C07BlanketMappingProbe/own-mapping (C07-fault-responses-blanket) caught, 27 of 411 rows violate; e.g. #2 own-mapping: violation:/c/k/one reporting HFault.Missing: type https://tools.ietf.org/html/rfc
C07BlanketMappingProbe/own-set-keeps-mapping (C07-fault-responses-blanket) caught, 64 of 411 rows violate; e.g. #1 own-set-keeps-mapping: violation:/m/c7/k/two reporting HFault.Missing: status 404, e
C07BlanketMappingProbe/enum-needs-own-mapping (C07-fault-responses-blanket) caught, 66 of 411 rows violate; e.g. #0 enum-needs-own-mapping: violation:host started, though the README says this set sto
C07BlanketMappingProbe/member-without-kind-stops-start (C07-fault-responses-blanket) caught, 33 of 411 rows violate; e.g. #7 member-without-kind-stops-start: violation:host started, though the README
C07BlanketMappingProbe/mapping-refusal-stops-start (C07-fault-responses-blanket) caught, 70 of 411 rows violate; e.g. #4 mapping-refusal-stops-start: violation:host started, though the README says th
C08SameNameWarningProbe/bare-names-warn (C08-same-name-warning) caught, 90 of 302 rows violate; e.g. #0 bare-names-warn: violation:K3None: 1 endpoint(s) answer HFault.Clash and KFault.Clash alike as
C08SameNameWarningProbe/qualified-chain-warns (C08-same-name-warning) caught, 74 of 302 rows violate; e.g. #8 qualified-chain-warns: violation:T3None: 1 endpoint(s) answer Twin.Same and Twin.Same ali
C08SameNameWarningProbe/warning-names-both (C08-same-name-warning) caught, 164 of 302 rows violate; e.g. #0 warning-names-both: violation:K3None: the warning does not name KFault.Clash: HTTP: GET /s/
C08SameNameWarningProbe/qualified-names-fix (C08-same-name-warning) caught, 74 of 302 rows violate; e.g. #8 qualified-names-fix: violation:K3None (qualified names, none left out): the errors are writ
C08SameNameWarningProbe/leave-one-out-fixes (C08-same-name-warning) caught, 138 of 302 rows violate; e.g. #1 leave-one-out-fixes: violation:T2Wo (qualified names, one left out): the errors are writte
C12DescribedConverterProbe/own-converter-describes-itself (C12-described-converter) caught, 115 of 301 rows violate; e.g. #1 own-converter-describes-itself: violation:/s/coord/c: a property is {}, th
C12DescribedConverterProbe/options-describe (C12-described-converter) caught, 30 of 301 rows violate; e.g. #26 options-describe: violation:/rg/s/coord/m: a property is {}, the JsonSchemaDescriptions
C12DescribedConverterProbe/every-position (C12-described-converter) caught, 145 of 301 rows violate; e.g. #1 every-position: violation:/s/coord/c: a list's items is missing from the body schema: {"ad
C12DescribedConverterProbe/parameter-keeps-aspnet (C12-described-converter) caught, 73 of 301 rows violate; e.g. #2 parameter-keeps-aspnet: violation:/rg/s/dec/m: the query parameter q is described a
C13PatchRequiredProbe/doc-required-where-rejected (C13-patch-required) caught, 35 of 296 rows violate; e.g. #4 doc-required-where-rejected: violation:/s/ctor/m: the constructor-parameter patch floor_
C13PatchRequiredProbe/doc-optional-where-absent (C13-patch-required) caught, 120 of 296 rows violate; e.g. #0 doc-optional-where-absent: violation:/px/s/init/c: the init patch FLOOR is in required un
C13PatchRequiredProbe/absent-is-default (C13-patch-required) caught, 200 of 296 rows violate; e.g. #0 absent-is-default: violation:/px/s/init/c: a body without the init patch was answered 200 Null, n
C14DeclaredProblemProbe/declared-describes-fault (C14-declared-problem) caught, 30 of 421 rows violate; e.g. #15 declared-describes-fault: violation:/px/c/d/pp/two 404: response schema does not descr
C14DeclaredProblemProbe/declared-names-types (C14-declared-problem) caught, 77 of 421 rows violate; e.g. #1 declared-names-types: violation:/c/d/prt/dup 404: description "Not Found" names problem typ
C14DeclaredProblemProbe/ascending-with-declared (C14-declared-problem) caught, 105 of 421 rows violate; e.g. #1 ascending-with-declared: violation:/c/d/prt/dup: responses 204,404,400,403,409,412,422,
C14DeclaredProblemProbe/application-json-declared (C14-declared-problem) caught, 38 of 421 rows violate; e.g. #1 application-json-declared: violation:/c/d/prt/dup 404: description "Not Found" names p
C15MvcFaultStatusDocProbe/builder-reaches-doc (C15-mvc-fault-status-doc) caught, 24 of 405 rows violate; e.g. #30 builder-reaches-doc: violation:/c/s/a/two: document lists 400,403,404,409,412,422,503
C15MvcFaultStatusDocProbe/without-faults-leaves-doc (C15-mvc-fault-status-doc) caught, 45 of 405 rows violate; e.g. #20 without-faults-leaves-doc: violation:/m/c15/s/a/one: document lists 400,403,404
C15MvcFaultStatusDocProbe/status-per-scope (C15-mvc-fault-status-doc) caught, 76 of 405 rows violate; e.g. #0 status-per-scope: violation:/c/s/a/decl: document lists 400,403,404,409,412,422,429,503,
C16OptionsAgreementProbe/differing-refused (C16-options-agreement) caught, 42 of 296 rows violate; e.g. #6 differing-refused: violation:Coord: the host has controllers, its minimal-API options alone
C16OptionsAgreementProbe/error-names-type-and-descriptions (C16-options-agreement) caught, 42 of 296 rows violate; e.g. #6 error-names-type-and-descriptions: violation:Coord: the refusal names the ty
C16OptionsAgreementProbe/patch-held-refused (C16-options-agreement) caught, 42 of 296 rows violate; e.g. #6 patch-held-refused: violation:a type held only inside a patch, CoordP: the host has control
C16OptionsAgreementProbe/same-converters-build (C16-options-agreement) caught, 86 of 296 rows violate; e.g. #1 same-converters-build: violation:document: AddTypeKit() fails (InvalidOperationException
C16OptionsAgreementProbe/minimal-host-not-compared (C16-options-agreement) caught, 25 of 296 rows violate; e.g. #0 minimal-host-not-compared: violation:document: AddTypeKit() fails (InvalidOperationE
C21OpenapiSchemaParityProbe/the-underlying-types-schema (C21-openapi-schema-parity) caught, 10 of 294 rows violate; e.g. #15 the-underlying-types-schema: violation:schema differs for /mvc/bind/header
C21OpenapiSchemaParityProbe/container-elements (C21-openapi-schema-parity) caught, 16 of 294 rows violate; e.g. #0 container-elements: violation:schema differs for /schema/plain vs /schema/vo at $.re
C21OpenapiSchemaParityProbe/dictionary-key (C21-openapi-schema-parity) caught, 1 of 294 rows violate; e.g. #137 dictionary-key: violation:schema differs for /rg/schema/plain vs /rg/schema/vo at $.req
C21OpenapiSchemaParityProbe/every-document-version (C21-openapi-schema-parity) caught, 52 of 294 rows violate; e.g. #5 every-document-version: violation:schema differs for /bind/form/plain vs /bind/f
C21OpenapiSchemaParityProbe/nullable-admits-null-as-the-wrapped-does (C21-openapi-schema-parity) caught, 7 of 294 rows violate; e.g. #63 nullable-admits-null-as-the-wrapped-does: violation:schema dif
C01DecimalSpellingProbe/smallest-scale (C01-decimal-spelling) caught, 57 of 117 rows violate; e.g. #0 smallest-scale: violation:write: Number 0.00, expected the smallest scale 0, in {"$id":"1","p":0.
C01DecimalSpellingProbe/number-is-a-number (C01-decimal-spelling) caught, 43 of 117 rows violate; e.g. #0 number-is-a-number: violation:write: String 0, expected the Number 0, in {"$id":"1","p":"0"}
C01DecimalSpellingProbe/string-is-a-string (C01-decimal-spelling) caught, 54 of 117 rows violate; e.g. #1 string-is-a-string: violation:write: Number 7.8, expected the String 7.8, in {"P_x":{"$type":
C01DecimalSpellingProbe/reads-a-number-or-a-string (C01-decimal-spelling) caught, 101 of 117 rows violate; e.g. #0 reads-a-number-or-a-string: violation:read string: the decimal with DecimalNumberJso
C01DecimalSpellingProbe/keeps-the-scale-it-reads (C01-decimal-spelling) caught, 64 of 117 rows violate; e.g. #0 keeps-the-scale-it-reads: violation:read number: read 0, expected 0.00 with its scale t
C01DecimalSpellingProbe/covers-every-decimal (C01-decimal-spelling) caught, 4 of 117 rows violate; e.g. #32 covers-every-decimal: violation:write: key 7.8000000000000000000000000000, expected 7.8, in
C10PreserveGuardProbe/refused-where-references-could-be-written (C10-preserve-guard) caught, 4 of 306 rows violate; e.g. #221 refused-where-references-could-be-written: violation:write: expected NotS
C10PreserveGuardProbe/value-converters-unaffected (C10-preserve-guard) caught, 4 of 306 rows violate; e.g. #59 value-converters-unaffected: violation:write: the value object threw NotSupportedExcepti
C10PreserveGuardProbe/own-converters-unaffected (C10-preserve-guard) caught, 15 of 306 rows violate; e.g. #1 own-converters-unaffected: violation:write: the value object threw NotSupportedException:
C10PreserveGuardProbe/ignore-cycles-unaffected (C10-preserve-guard) caught, 4 of 306 rows violate; e.g. #72 ignore-cycles-unaffected: violation:write: both threw, not alike: the wrapped type write re
L01JsonBytesProbe/as-a-dictionary-key-too (L01-json-bytes) caught, 11 of 581 rows violate; e.g. #56 as-a-dictionary-key-too: violation:JSON differs: wrapped {"$id":"1","p":{"$id":"2","own:"2026-10-1
L01JsonBytesProbe/the-underlying-types-json (L01-json-bytes) caught, 12 of 581 rows violate; e.g. #11 the-underlying-types-json: violation:JSON differs: wrapped {"p":"7"} vs value object {"p":7}
L01JsonBytesProbe/read-as-the-underlying-type-reads (L01-json-bytes) caught, 26 of 581 rows violate; e.g. #20 read-as-the-underlying-type-reads: violation:input valid: wrapped read it, value object t
L02NoQuoteProbe/parsing-never-repeats (L02-no-quote) caught, 183 of 586 rows violate; e.g. #0 parsing-never-repeats: violation:TryParse threw FormatException on 'S3CR3T-xyz': The input string 'S3CR3T
L02NoQuoteProbe/json-never-repeats (L02-no-quote) caught, 199 of 586 rows violate; e.g. #0 json-never-repeats: violation:JSON read: JsonException quotes the input: V_validated_struct_ulong could not
L02NoQuoteProbe/no-inner-that-quotes (L02-no-quote) caught, 22 of 586 rows violate; e.g. #5 no-inner-that-quotes: violation:Parse: inner FormatException quotes the input
L06OffTheWireProbe/never-written (L06-off-the-wire) caught, 268 of 325 rows violate; e.g. #0 never-written: violation:write: Option<V_validated_struct_TimeSpan> was written: {"P":[{}]}
L06OffTheWireProbe/never-read (L06-off-the-wire) caught, 267 of 325 rows violate; e.g. #0 never-read: violation:read duplicate-property: expected NotSupportedException naming Option<V_validated_struc
C02OptionalTextProbe/hidden-is-an-error-in-both (C02-optional-text) caught, 7 of 46 rows violate; e.g. #3 hidden-is-an-error-in-both: violation:AsOptionalIdentifier(ZWSP-only) = None; expected Error:
C02OptionalTextProbe/required-blank-is-an-error (C02-optional-text) caught, 11 of 46 rows violate; e.g. #2 required-blank-is-an-error: violation:AsIdentifier(spaces) = ok:; expected Error:Blank
C02OptionalTextProbe/optional-blank-is-no-value (C02-optional-text) caught, 14 of 46 rows violate; e.g. #0 optional-blank-is-no-value: violation:AsOptionalText(nbsp-only) = Error:Blank; expected None
C02OptionalTextProbe/null-is-blank (C02-optional-text) caught, 6 of 46 rows violate; e.g. #6 null-is-blank: violation:AsText(null) = Error:Hidden; expected Error:Blank
C11ParseShapesBuildProbe/implicit-result (C11-parse-shapes-build) caught, 28 of 247 rows violate; e.g. #2 implicit-result: violation:the value object over a implicit-result type has parsing: Parse, T
C11ParseShapesBuildProbe/ref-text (C11-parse-shapes-build) caught, 22 of 247 rows violate; e.g. #4 ref-text: violation:the value object over a ref-text type has parsing: Parse, TryParse, IParsable, a
C11ParseShapesBuildProbe/abstract-string-constructor (C11-parse-shapes-build) caught, 22 of 247 rows violate; e.g. #3 abstract-string-constructor: violation:the value object over a abstract-string-ct
C11ParseShapesBuildProbe/parsing-where-it-can-be-parsed (C11-parse-shapes-build) caught, 16 of 247 rows violate; e.g. #37 parsing-where-it-can-be-parsed: violation:type-converter: the value object ov
L03OneRuleProbe/parsing-runs-create (L03-one-rule) caught, 109 of 586 rows violate; e.g. #3 parsing-runs-create: violation:ISpanParsable accepted 'False' that Create refuses
L03OneRuleProbe/tryfrom-runs-create (L03-one-rule) caught, 30 of 586 rows violate; e.g. #7 tryfrom-runs-create: violation:TryFrom accepted the value Create refuses
L03OneRuleProbe/json-runs-create (L03-one-rule) caught, 157 of 586 rows violate; e.g. #0 json-runs-create: violation:JSON read accepted the value Create refuses: {"P_x":0}
L03OneRuleProbe/refusals-name-the-fault (L03-one-rule) caught, 196 of 586 rows violate; e.g. #0 refusals-name-the-fault: violation:JsonException does not name the fault: V_validated_struct_ulong refu
L04RoundTripProbe/invariant-whatever-the-culture (L04-round-trip) caught, 21 of 290 rows violate; e.g. #15 invariant-whatever-the-culture: violation:ToString(): text '7,80' differs from the wrapped t
L04RoundTripProbe/the-type-converter-writes-the-same (L04-round-trip) caught, 5 of 290 rows violate; e.g. #14 the-type-converter-writes-the-same: violation:type-converter: text '15.10.2026 08:30:00'
L04RoundTripProbe/text-reads-back (L04-round-trip) caught, 1 of 290 rows violate; e.g. #64 text-reads-back: violation:type-converter: '7.80' read back as 780, expected 7.80
L04RoundTripProbe/json-refusal-is-a-json-exception (L04-round-trip) caught, 45 of 290 rows violate; e.g. #10 json-refusal-is-a-json-exception: violation:input empty-string at the root: the JsonExcept

Verdicts

dotnet tools/BuildVerdict/bin/Release/net10.0/build-verdict.dll test --project P:

  • CodoMetis.TypeKit.Prober.Tests: TESTS OK: 257 passed, 0 skipped, CodoMetis.TypeKit.Prober.Tests, 11s 781ms
  • CodoMetis.TypeKit.Conventions.Tests: TESTS OK: 1491 passed, 0 skipped, CodoMetis.TypeKit.Conventions.Tests, 12s 154ms
  • CodoMetis.TypeKit.Facts: TESTS OK: 87 passed, 9 skipped, CodoMetis.TypeKit.Facts, 941ms
  • Facts' 9 skips are the same as an earlier run on this host's release/2.2.0.
  • replay --all, per family: http 13 passed, json 5, text 4, build 7, ef 16; status: 0 open findings, every obligation of every claim credited (0 uncredited).

Noticed, not changed

  • The EF twins (ef.cs) are as they were. EfEntry.Plain is the type From takes; an EF probe's twin is the entity's own wrapped-type property, declared beside the value object by the probe (column parity), not a catalogue type, and a class value object's column nullability is EF's own rule. Giving EF twins at the value object's nullability is its own step, with an EF replay.
  • What the regenerated rows of the reverted probes showed (those rows are not committed, so nothing below is in the corpus; reported here as candidates for whoever regenerates them next, untriaged):
    • C21, under OpenAPI 2.0, with a class value object over a struct at the root: ASP.NET writes the twin's nullable body (double?, int?) without pattern, the value object keeps it (rows 6, 143 of the regenerated file; G1 and KB-001's territory). Also a validated struct over string as a nullable property (the twin {"type":"string"}, the value object {}, row 115), a validated class over Uri (the twin's x-nullable, row 181), a sealed record class over char at the root (format: char alone against the value object's type: string, minLength/maxLength 1, row 282), a record class bound from a form (the twin's A/B properties, rows 53, 208), Instant as a dictionary value (row 134).
    • C10, row 266: an array of value objects over byte, under default-with-renaming-modifier with input.json=metadata-property, does not read back its own document ("V_record_struct_byte could not read the JSON value as Byte"), ignore-cycles-unaffected.
  • .claude/skills/probe-triage still lists "a context type the source-generated ProbeContext does not list" among harness errors; that is now what JsonRow.Listed raises, so it stays true.

🤖 Generated with Claude Code

CaffeinatedCoder and others added 2 commits October 17, 2026 17:45
…, workarounds removed

Every two-sided comparison builds both sides in one place, Sides.Twins, which holds the
precondition that the twin and the value object hold the same shape (Equivalence.Shapes,
now with nullability: both admit null or neither, a struct value object over a class the one
difference no plain type mirrors) and throws a harness error otherwise. The registry's Plain
is the twin shapes.cs writes, so the http hosts' plainnull endpoints and Hx.PlainNull go.

json.TypeInfoResolver=source-generated-context is strict: ProbeContext alone, no fallback,
and a shape it does not list is a harness error. json.TypeInfoResolverChain=context-then-default
is the configuration with the reflection fallback: after the context, System.Text.Json refuses
only the context's own instance in its chain, which JsonRow inserted a second time. The six
exclusions that read that refusal as STJ refusing any chain after a context are removed.

Removed: JsonRow.ListedInContext's +reflection-fallback substitution, the per-claim contexts
JCtx applied, SchemaClaim's Combine(ProbeContext, SchemaJson) with the fallback removed from
the evidence, and FaultClaim's substitution of the payload to property-of-class.

Rows: regenerated with `prober rows --probe` for the 20 probes whose claims take
json.TypeInfoResolver; kept where they cover as many pairs (C04, C06, C08, C12, C13, C16, L04),
the old rows kept elsewhere, C21 topped up with one row for the pair the old rows left.
FaultClaim compares the two documents' refusals without their byte position (Hx.Unplaced),
as HttpHost does: C06's regenerated rows 73 and 157 refused alike at bytes 32 and 41.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
shapes.cs writes each value object's twin, the wrapped type at its nullability (T? for a class
value object over a struct, a string for the closed set), its polymorphic stand-ins outside the
nullable context as the containers are, and one ProbeContext listing every value object, every
struct one made nullable and every twin at the root and at each shape.position in each
shape.containing, plus the Option and Result carriers L06's rows hold (--l06). json-family.cs
stops writing the per-claim contexts of C01, C10 and L06 and writes L06's stand-ins only
(mode stand-ins). GeneratedHarnessTests' table takes the new arguments.

One declaration: a second partial part of ProbeContext fails System.Text.Json's generator
(duplicate hint names). Measured on Linux x64, a Samples rebuild after a harness change: 38 s
before, 73 s with this context (8974 types), 3 min 49 s in a trial with every carrier at every
position (23266 types), so L06's carriers are listed from its rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@CaffeinatedCoder
CaffeinatedCoder merged commit ac4c561 into release/2.2.0 Oct 17, 2026
13 checks passed
@CaffeinatedCoder
CaffeinatedCoder deleted the test/ws2-twins-contexts branch October 17, 2026 18:52
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