Repository navigation
test: generate the twins and one ProbeContext with the shapes (WS2) - #59
Merged
Merged
Conversation
…, 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>
This was referenced Oct 17, 2026
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.
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.cswrites 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 overint); a struct value object over a struct, and any value object over a class, faces the wrapped type itself.Entry.Plainis that twin. The twins' polymorphic stand-ins (PBP_int_Nholdingint?) are generated too, and every stand-in is declared outside the nullable context, as the containers are, sojson.RespectNullableAnnotationsreads no annotation one side has and the other lacks.a-closed-set-is-a-string-enum, L01 citesa-closed-set-key-is-its-name. No new not-promised entry.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 runsEquivalence.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 aPatch<T>'s value, never null on either side, are the exceptions). A mismatch throwsInputsNotEquivalent, a harness error; the host-start catches let it through rather than reading it as "host did not start".Hx.PlainNulland the http hosts'plainnullendpoints 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).http.nullability=nullableholds), and every twin, at the root and at eachshape.positionin eachshape.containing, plus the Option and Result carriers L06's rows hold under the context (--l06): 8974 types.json.TypeInfoResolver=source-generated-contextis now strict:ProbeContextalone, no reflection. A shape the context does not list is a harness error (JsonRow.Listed,JCtx.Apply), never a fallback.json.TypeInfoResolverChain=context-then-default, the existing axis value: the context, thenDefaultJsonTypeInfoResolver. 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 insertedProbeContext.Defaulta 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).JsonRow.ListedInContext's+reflection-fallbacksubstitution; the per-claim contextsC01JsonContext,C10JsonContext,L06JsonContext(json-family.csmodecontextsbecomesstand-ins, L06's polymorphic stand-ins only);SchemaClaim.StrictSourceGen'sCombine(ProbeContext, SchemaJson)and its removal of the fallback from the evidence (the hosts appendSchemaJson, the application's own types, to the chain, as an application does; C08's alike host now does too, forProblemDetails);FaultClaim.StrictContext's substitution of the payload to property-of-class.NoteKeyskeeps every member:ctx-fallbackandschema:compared-with-nullable-wrappedare marked "not called since".3. Rows (
prober rows --probe, then a replay of each).prober rows --probefor the 20 probes whose claims takejson.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.rows --claimrow 31: source-generated-context withcontext-then-default, a record struct overbool, list-element, OpenAPI 3.0) for the one pair the old rows left, the context with the fallback.rows --claim C21printed 301 rows for C21's 358 scattered uncovered pairs; the rest are not this step's.L06's rows are its old ones, soShapes.g.cs' carriers are what they were listed from.4. The guard.
GeneratedHarnessTests' table:Shapes.g.cstakes--l06 rows/L06OffTheWireProbe.json.gz,JsonFamilyShapes.g.cstakesstand-ins. Proven by seeding:[JsonSerializable(typeof(Cn_class_P<V_record_struct_inT>)…in the committedShapes.g.csfailsThe_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 filerows --probewrote; "after" the committed state.The pairs with
json.TypeInfoResolver=source-generated-contextCounted 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.Before and after, per probe
replay --allper family from an empty state, before onrelease/2.2.0at e01932b, after on this branch. http, json and text, the families whose claims take the resolver (C02 and C11 ride in the text family):"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:mvc bind form, refused value): before, the row ran on the reflection fallback (+reflection-fallbacksubstituted) and MVC answered 400. Strict now, MVC cannot write itsValidationProblemDetailsand answers 500, as it does for the twin without TypeKit:framework-refusesagainstfact:KB-041.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.cscompile into the same Samples assembly), so both were replayed: 7 and 16 probes pass, rows and checks identical to before, row for row:New violations: none in the committed state. Every one met on the way was the harness's, fixed here: context gaps (
V?underhttp.nullability=nullable, a rootdecimal?whose metadata asks the options fordecimal, L06's nullable carriers listed through the contract build), the stand-ins' nullable annotations, three C21 schema comparisons that lost their normalisation or citation withplainnull(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}]}, citeddocs: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.Samplesin the replay's worktree,Probes/Row.cstouched, 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 ofProbeContextis 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 Pfor 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.BaseProbe (C07-fault-responses-blanket)C03ComposedProbe/each-error-own-set (C03-composed-faults)C03ComposedProbe/detail-and-rule-intact (C03-composed-faults)C03ComposedProbe/statuses-ascending (C03-composed-faults)C03ComposedProbe/shared-status-types (C03-composed-faults)C03ComposedProbe/unmapped-set-stops-start (C03-composed-faults)C04WithoutFaultsProbe/doc-omits-left-out (C04-without-faults)C04WithoutFaultsProbe/reporting-left-out-throws (C04-without-faults)C04WithoutFaultsProbe/narrows-wider-status (C04-without-faults)C04WithoutFaultsProbe/left-out-everywhere-stops-start (C04-without-faults)C05RuleProbe/rule-qualified (C05-with-rule)C05RuleProbe/nested-chain (C05-with-rule)C05RuleProbe/stays-the-errors (C05-with-rule)C05RuleProbe/unnamed-value-throws (C05-with-rule)C05RuleProbe/no-namespace (C05-with-rule)C06FaultKindsProbe/kind-gives-status (C06-fault-kinds)C06FaultKindsProbe/description-as-detail (C06-fault-kinds)C06FaultKindsProbe/no-description-no-detail (C06-fault-kinds)C06FaultKindsProbe/member-must-have-kind (C06-fault-kinds)C07BlanketMappingProbe/by-kind (C07-fault-responses-blanket)C07BlanketMappingProbe/own-mapping (C07-fault-responses-blanket)C07BlanketMappingProbe/own-set-keeps-mapping (C07-fault-responses-blanket)C07BlanketMappingProbe/enum-needs-own-mapping (C07-fault-responses-blanket)C07BlanketMappingProbe/member-without-kind-stops-start (C07-fault-responses-blanket)C07BlanketMappingProbe/mapping-refusal-stops-start (C07-fault-responses-blanket)C08SameNameWarningProbe/bare-names-warn (C08-same-name-warning)C08SameNameWarningProbe/qualified-chain-warns (C08-same-name-warning)C08SameNameWarningProbe/warning-names-both (C08-same-name-warning)C08SameNameWarningProbe/qualified-names-fix (C08-same-name-warning)C08SameNameWarningProbe/leave-one-out-fixes (C08-same-name-warning)C12DescribedConverterProbe/own-converter-describes-itself (C12-described-converter)C12DescribedConverterProbe/options-describe (C12-described-converter)C12DescribedConverterProbe/every-position (C12-described-converter)C12DescribedConverterProbe/parameter-keeps-aspnet (C12-described-converter)C13PatchRequiredProbe/doc-required-where-rejected (C13-patch-required)C13PatchRequiredProbe/doc-optional-where-absent (C13-patch-required)C13PatchRequiredProbe/absent-is-default (C13-patch-required)C14DeclaredProblemProbe/declared-describes-fault (C14-declared-problem)C14DeclaredProblemProbe/declared-names-types (C14-declared-problem)C14DeclaredProblemProbe/ascending-with-declared (C14-declared-problem)C14DeclaredProblemProbe/application-json-declared (C14-declared-problem)C15MvcFaultStatusDocProbe/builder-reaches-doc (C15-mvc-fault-status-doc)C15MvcFaultStatusDocProbe/without-faults-leaves-doc (C15-mvc-fault-status-doc)C15MvcFaultStatusDocProbe/status-per-scope (C15-mvc-fault-status-doc)C16OptionsAgreementProbe/differing-refused (C16-options-agreement)C16OptionsAgreementProbe/error-names-type-and-descriptions (C16-options-agreement)C16OptionsAgreementProbe/patch-held-refused (C16-options-agreement)C16OptionsAgreementProbe/same-converters-build (C16-options-agreement)C16OptionsAgreementProbe/minimal-host-not-compared (C16-options-agreement)C21OpenapiSchemaParityProbe/the-underlying-types-schema (C21-openapi-schema-parity)C21OpenapiSchemaParityProbe/container-elements (C21-openapi-schema-parity)C21OpenapiSchemaParityProbe/dictionary-key (C21-openapi-schema-parity)C21OpenapiSchemaParityProbe/every-document-version (C21-openapi-schema-parity)C21OpenapiSchemaParityProbe/nullable-admits-null-as-the-wrapped-does (C21-openapi-schema-parity)C01DecimalSpellingProbe/smallest-scale (C01-decimal-spelling)C01DecimalSpellingProbe/number-is-a-number (C01-decimal-spelling)C01DecimalSpellingProbe/string-is-a-string (C01-decimal-spelling)C01DecimalSpellingProbe/reads-a-number-or-a-string (C01-decimal-spelling)C01DecimalSpellingProbe/keeps-the-scale-it-reads (C01-decimal-spelling)C01DecimalSpellingProbe/covers-every-decimal (C01-decimal-spelling)C10PreserveGuardProbe/refused-where-references-could-be-written (C10-preserve-guard)C10PreserveGuardProbe/value-converters-unaffected (C10-preserve-guard)C10PreserveGuardProbe/own-converters-unaffected (C10-preserve-guard)C10PreserveGuardProbe/ignore-cycles-unaffected (C10-preserve-guard)L01JsonBytesProbe/as-a-dictionary-key-too (L01-json-bytes)L01JsonBytesProbe/the-underlying-types-json (L01-json-bytes)L01JsonBytesProbe/read-as-the-underlying-type-reads (L01-json-bytes)L02NoQuoteProbe/parsing-never-repeats (L02-no-quote)L02NoQuoteProbe/json-never-repeats (L02-no-quote)L02NoQuoteProbe/no-inner-that-quotes (L02-no-quote)L06OffTheWireProbe/never-written (L06-off-the-wire)L06OffTheWireProbe/never-read (L06-off-the-wire)C02OptionalTextProbe/hidden-is-an-error-in-both (C02-optional-text)C02OptionalTextProbe/required-blank-is-an-error (C02-optional-text)C02OptionalTextProbe/optional-blank-is-no-value (C02-optional-text)C02OptionalTextProbe/null-is-blank (C02-optional-text)C11ParseShapesBuildProbe/implicit-result (C11-parse-shapes-build)C11ParseShapesBuildProbe/ref-text (C11-parse-shapes-build)C11ParseShapesBuildProbe/abstract-string-constructor (C11-parse-shapes-build)C11ParseShapesBuildProbe/parsing-where-it-can-be-parsed (C11-parse-shapes-build)L03OneRuleProbe/parsing-runs-create (L03-one-rule)L03OneRuleProbe/tryfrom-runs-create (L03-one-rule)L03OneRuleProbe/json-runs-create (L03-one-rule)L03OneRuleProbe/refusals-name-the-fault (L03-one-rule)L04RoundTripProbe/invariant-whatever-the-culture (L04-round-trip)L04RoundTripProbe/the-type-converter-writes-the-same (L04-round-trip)L04RoundTripProbe/text-reads-back (L04-round-trip)L04RoundTripProbe/json-refusal-is-a-json-exception (L04-round-trip)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 781msCodoMetis.TypeKit.Conventions.Tests: TESTS OK: 1491 passed, 0 skipped, CodoMetis.TypeKit.Conventions.Tests, 12s 154msCodoMetis.TypeKit.Facts: TESTS OK: 87 passed, 9 skipped, CodoMetis.TypeKit.Facts, 941msrelease/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
ef.cs) are as they were.EfEntry.Plainis the typeFromtakes; 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.double?,int?) withoutpattern, the value object keeps it (rows 6, 143 of the regenerated file; G1 and KB-001's territory). Also a validated struct overstringas a nullable property (the twin{"type":"string"}, the value object{}, row 115), a validated class overUri(the twin'sx-nullable, row 181), a sealed record class overcharat the root (format: charalone against the value object'stype: string, minLength/maxLength 1, row 282), a record class bound from a form (the twin'sA/Bproperties, rows 53, 208),Instantas a dictionary value (row 134).byte, underdefault-with-renaming-modifierwithinput.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-triagestill lists "a context type the source-generatedProbeContextdoes not list" among harness errors; that is now whatJsonRow.Listedraises, so it stays true.🤖 Generated with Claude Code