Skip to content

build: answer the gate's refusals with rows, declarations and exclusions; the gate passes - #62

Merged
CaffeinatedCoder merged 16 commits into
release/2.2.0from
build/gate-closes
Oct 18, 2026
Merged

CaffeinatedCoder merged 16 commits into
release/2.2.0from
build/gate-closes

Conversation

@CaffeinatedCoder

@CaffeinatedCoder CaffeinatedCoder commented Oct 18, 2026 •

Copy link
Copy Markdown
Owner

The 17 refusals prober gate --since v2.1.0 gave at 938d8f8, answered with what decision 97 allows: probes' rows, declarations of inapplicability and exclusions with their reasons. There is no waiver, no workflow edit, no message matching and no product change.

Verdict: the gate passes. prober gate --since v2.1.0, after prober replay --all from an empty PROBE_STATE_DIR at cfe942a: "44 claim(s) touched and fully covered, every probe replayed and passing, no open finding." All 17 refusals are answered with probes' rows, declarations, exclusions and one product fix (F4, #63). There is no waiver. Every probe's seeds were caught again: 170 of 170.

C21's last ten failing rows were triaged (below). Five of them were one product defect, F4, confirmed by the code-analyst and fixed in #63 and merged here through release/2.2.0. The other five, and four more that the fix exposed, are a harness gap, a new verified fact (KB-042) and entries the claim does not promise. Rows 435–453 cover C21's last pairs.

Before and after

prober replay --all from an empty state (PROBE_STATE_DIR in a scratch directory), then prober gate --since v2.1.0. Before: release/2.2.0 at 938d8f8. After: this branch at cfe942a (C21 finished after 53050b7; the earlier groups were measured at 53050b7). Both runs used the tools built in the worktree, on Linux x64 (32 threads).

Claim Uncovered before Uncovered after Answered by
C02-optional-text 7 0 5 by rows, 2 excluded
C03-composed-faults 2 0 2 excluded
C05-with-rule 2 0 2 excluded
C12-described-converter 821 0 647 by rows, 174 declared inapplicable
C13-patch-required 48 0 48 by rows
C16-options-agreement 455 0 283 by rows, 172 declared
C17-cmtk0014 41 0 41 by rows
C19-cmtk0010-kinds 50 0 50 by rows
C21-openapi-schema-parity 698 0 498 + 39 + 11 by rows, 166 declared; KB-042 and MaxDepth × record class declared (below)
C22-generator-refusals 621 0 607 once its footprint is recorded, 14 by rows
E02-value-translates-to-column 6 0 3 by rows, 3 excluded
L01-json-bytes 5 0 3 by rows, 2 excluded
L02-no-quote 74 0 65 by rows, 6 declared, 3 excluded
L03-one-rule 5 0 3 by rows, 2 excluded
L04-round-trip 2 0 2 excluded
L06-off-the-wire 1 0 1 by rows
all 2,838 (17 refusals) 0

prober status at 53050b7: 0 open findings, 0 uncredited obligations in all 44 claims. docs-query check at cfe942a: 0 problems (42 facts, 17 fact citations).

Commits

Commit Group Pairs recovered
1f1b7d1 build: a probe that runs nothing under src/ is recorded by what it watches 1, C22 C22 607
76b17d2 test: the document obligations do not apply under Preserve, and rows for the rest of C12, C13, C16 and C21 2 C12 821, C13 48, C16 455, C21 664
9b6f58e docs: C21 does not promise the twin's schema where a nullable value object admits null and ASP.NET drops it C21 triage, step 3 (row 216)
7b0edf0 test: C21's schema comparison cites KB-001 …, decision 36 …, and cannot tell a form-bound record class C21 triage, steps 1–3 (rows 62, 113, 216, 282)
ed1069b test: C17's and C19's rows written again, without the refused kinds they substituted 3 C17 41, C19 50
dfc841a test: L02's parsing obligations do not apply to TryFrom or formatting, and its JSON check reads a value of another kind 4 L02 74
689fed9 test: E02 compares an enum with >= as C# does, excludes >= over string, bool and Uri, … 5 E02 6
53050b7 test: rows and exclusions for the scattered pairs of C02, C03, C05, L01, L03, L04, L06 and C22 6 C02 7, C03 2, C05 2, L01 5, L03 5, L04 2, L06 1, C22 14
4675d2e test: KB-042, ASP.NET refuses a char's schema under WriteAsString, and C21's obligations do not apply there C21 triage, step 2 (row 408)
82756b9 docs: C21 does not promise the twin's schema for an enum component's first-use null, a 2.0 nullable element, or a library's converter C21 triage, step 3 (rows 196, 358, 422)
2fab368 test: C21's schema comparison cites KB-026 after the null normalisation and for an element, and KB-027 and a library's converter for a property C21 triage, step 3 (rows 196, 358, 386, 422)
c8b4575 … 059c0a0 (#63, via cfe942a) fix: a nullable value object that is a component keeps its null as a get-only property, a field and an oblivious one C21 triage, step 4: F4 (rows 38, 170, 184, 330, 415)
ec7a4f9 test: C21 compares schemas under the media types both bodies list where MVC's formatters decide them, and tells KB-026 by the null flag beside a format after F4: steps 1 and 3 (rows 38, 178, 181, 243)
7565175 test: rows for C21's last uncovered pairs, and KB-026 told where the twin is a component itself 7 C21 27 (rows 435–448; row 444)
562c367 test: C21's obligations do not apply to a wrapped record class under MaxDepth=3, and rows for its last 12 pairs 7 C21 12 (1 declared, rows 449–453)
60de568 docs: the roadmap's 2.2.0 gate item is done

1. C22: an empty footprint (build:)

The probe builds its consumer in a child process, so the coverage of its own process shows nothing under src/. prober footprint refused to record an empty footprint whatever the registry entry watched, which is why adding watched alone could not answer this refusal. C22's entry already watched what C18's does. footprint now records an empty footprint when the entry watches something, and still refuses when nothing ran and nothing is watched. A footprint that ran nothing under src/ is current by its watched paths. C22's entry now also watches the generators and the analyzers its consumer compiles against. Two new WatchedTests; each fails with its half of the change reverted, and the refusal test fails with the refusal removed. Its other 14 pairs had no row, and got rows in group 6.

2. C12, C13, C16, C21: documents both hosts refuse

Declaration (inapplicable) Obligations Cite Why it is true
json.ReferenceHandler=Preserve every obligation of C12, C16, C21; C13's doc-required-where-rejected and doc-optional-where-absent record:0099 At 938d8f8, from an empty state, ASP.NET alone also refused the document in every row under Preserve: C12 76, C13 71, C16 93, C21 63 rows, none written. C13's absent-is-default reads the wire and stays wanted.
json.MaxDepth=3 with source IDescribedJsonConverter or JsonSchemaDescriptions C12's four obligations record:0099 ASP.NET alone refused the coord host's document in every such row (106 rows at 938d8f8, 166 in the rows now), none written. Under the decimal-converter source it was written in every row (29 and 55).

Not declared: json.MaxDepth=3 on its own. The brief asked for it, but it would not be true. Under it, documents are written and compared in C12's decimal host and in 47 rows of C13, 91 of C16 and 59 of C21, and its pairs were covered already. Whether ASP.NET refuses a document under MaxDepth=3 depends on the depth of the row's shape, not on an axis, so those pairs needed more rows instead. No fact backs these declarations: KB-040 and KB-041 are about a converter's depth charge and MVC's 400, not the document. So they cite record 0099, which states the refusal. docs-query check stays at 0 problems.

Rows: rows --probe for the four probes, then rows appended from rows --claim until the pairs converged: C12 → 462 rows, C13 → 321, C16 298, C21 → 435. C13's rows --probe came out the same as before, because Preserve stays wanted there.

3. C17, C19

Both rows files were written again with rows --probe, which now leaves out of a row an axis none of whose values can join it (14 rows of each). Every row passes.

4. L02: the own-converter pairs of TryFrom and formatting

I chose a declaration, because no rows could reach these pairs. TryFrom, ToString-format and TryFormat-short-span reject no text: TryFrom answers None, and formatting reads no input. The probe already runs its parsing checks only for the five parsing entries, which are the axes both parsing obligations list. So parsing-never-repeats and no-inner-that-quotes declare themselves inapplicable at those three entries (docs:src/CodoMetis.TypeKit.Generators/README.md#when-text-is-rejected). Under an application's own converter, the JSON check is already not promised (not-promised:the-applications-own-converter). So those 6 pairs are wanted by no obligation. Every other pair of those entries stays wanted by json-never-repeats.

L02's JSON check put the secret only where the wrapped value stands, as a string. An unvalidated value object over string or Uri reads every string, so those rows ended inconclusive. Where every variant was read, the check now also sends an array holding the secret in that place, and requires the refusal not to quote it. 22 of those 26 rows now pass and none fails. Two exclusions: Populate with a reference handler, as twelve other claims already exclude it; and a record class as a dictionary key (below). 8 rows were set by hand from a passing row's settings, for pairs the covering array kept placing in rows that read nothing.

5. E02

The probe substituted where-equals for where-value-compare wherever Expression.GreaterThanOrEqual throws. "No >= operator" is true of string, bool and Uri, which are now excluded. It is not true of an enum: C# compares two enum values over the underlying type. So the probe now writes that comparison, and rows 216 and 217 (enum, flags enum) run it and pass. Row 215 is the group-by row for nested-two-levels × DateTimeOffset.

6. The scattered pairs

Rows for C02 (4), L01 (3, plus row 584 below), L03 (2), L06 (1; Shapes.g.cs written again by shapes.cs, since its context lists L06's carriers) and C22 (9), and the exclusions below.

Declarations and exclusions added

Claim Kind Settings Cite / reason
C12, C16, C21 (all obligations), C13 (two) inapplicable json.ReferenceHandler=Preserve record:0099; measured above
C12 (all four) inapplicable json.MaxDepth=3 × source IDescribedJsonConverter, JsonSchemaDescriptions record:0099; measured above
L02 (parsing-never-repeats, no-inner-that-quotes) inapplicable text.entry TryFrom, ToString-format, TryFormat-short-span docs:src/CodoMetis.TypeKit.Generators/README.md#when-text-is-rejected
C21 not-promised entry a-nullable-value-object-admits-null (http.nullability=nullable) decision 36: "A nullable value object admits null in the OpenAPI document wherever it appears"
C03, C05, L01, L02, L03, L04 exclude Populate × ReferenceHandler Preserve, IgnoreCycles System.Text.Json refuses Populate with a reference handler; JsonRow substitutes the default (the reason twelve claims already give)
C02 exclude entry AsText, AsOptionalText × globalization-invariant not-promised as-text-refused-in-globalization-invariant-mode: AsText throws there (docs/text.md#globalization-invariant-mode), and AsOptionalText calls it
E02 exclude where-value-compare × string, bool, Uri C# defines no >= for them
L02 exclude dictionary-key × own-record-class System.Text.Json writes a record class as a key only through an application's converter (NotSupportedException for the twin alike), where the JSON half is not promised; and no parsing

L01's dictionary-key × own-record-class pair is covered by a row, not excluded. Row 584 runs it under json.Converters=own-for-wrapped, the only converter under which System.Text.Json writes a record class as a key, and it passes.

C21: the rows that failed (probe-triage)

Every row failed in three try runs at 53050b7 before it was triaged. Rows 62, 113, 216 and 282 were triaged in 7b0edf0 and 9b6f58e (step 1: a form-bound record class; step 2: KB-001; step 3: decision 36). The other ten:

Row What differed Step Outcome
38, 170, 184, 330, 415 the value object's property was the bare $ref to its component where the twin's inline schema admitted null: a get-only property set through the constructor (170), a property typed by a type parameter (184, 330, 415), one without nullable annotations (38) 4 F4, a product defect, fixed in #63. ASP.NET 10 puts null around a component's reference only where NullabilityInfoContext's write state is nullable (TypeExtensions.ShouldApplyNullablePropertySchema). The twin's inline null comes from System.Text.Json's IsGetNullable/IsSetNullable. ASP.NET alone writes the same bare reference for the value object (side (1)); decision 36 says a nullable value object admits null wherever it appears. Confirmed by the code-analyst on the maintainer's machine; fixed in #63 (Found-By: PR) by the session that owns it, and merged here through release/2.2.0.
408 AddTypeKit() refused the document with a JsonException reading OpenApiJsonSchema, ASP.NET alone with MaxDepth's InvalidOperationException 2 New executable fact KB-042: under NumberHandling=WriteAsString, ASP.NET refuses any document whose schema holds a char (the exporter's minLength/maxLength reach its reader as strings), measured without TypeKit. Both hosts refuse; TypeKit meets that refusal first, ASP.NET alone meets MaxDepth first at another endpoint. Each C21 obligation declares json.NumberHandling=WriteAsString × shape.wrapped=char inapplicable, fact:KB-042 (4675d2e). The code-analyst refuted it as a defect on the same grounds.
196 the twin's enum component lists null, the value object's doesn't, at a polymorphic derived type's non-nullable property 3 KB-027 (executable): an enum component lists the null of its first use "for its non-nullable uses too". New entry enum-component-lists-the-null-of-its-first-use; the KB-027 branch now takes a property as well as a parameter.
358 a dictionary's nullable value: the value object's is {"allOf":[{"x-nullable":true}]} under 2.0, the twin's bool? keeps its type 3 The AspNetCore README now says a nullable element loses its type under 2.0 as a nullable property does; new entry openapi-2-nullable-element-loses-its-type, told by structure (side (1) drops the element).
386 under 2.0, after the null-spelling normalisation, the value object's property lacks the type the twin's has 3 KB-026 (openapi-2-nullable-component-loses-its-type): the property is the null flag beside the wrapped type's format. Told by structure; the same branch now also covers 178, 181, 243 and 444.
422 a list of Instant: ASP.NET drops the twin's items, the value object's are put back 3 The README now says ASP.NET can't see through a converter a library registers either (NodaTime's); new entry a-librarys-converter-for-the-wrapped-type, side (1) showing the same.

What F4's fix exposed, each failing in try with the fix applied:

  • Row 38 (step 1): the next difference was the MVC body's media types. MVC's XmlSerializerInputFormatter reads a record class value object, not the Uri it wraps. ASP.NET alone lists the same media types for each side. The harness now compares the schemas under the media types both list, but only where side (1) lists each side's media types the same way, so a media type TypeKit lost still fails.
  • Rows 178, 181, 243 and 444 (step 3, KB-026): with F4's null in place, OpenAPI 2.0 writes those properties as the null flag, keeping TypeKit's format, as the README says.

A leftover to drop with the next harness change. At the merge of release/2.2.0 (cfe942a), HttpBaseline.SchemaParity kept #63's own 2.0 citation branch and its helper TheNullFlagWithoutItsReference beside this branch's structural KB-026 branch, which covers the same rows. Both cite openapi-2-nullable-component-loses-its-type, so the extra branch is redundant and harmless. Dropping it now would change the harness, which is in every probe's identity, and re-stale every probe's seeds (about four hours), so it stays until the harness next changes.

C21's last pairs

Step Uncovered after How
after the triage and F4 39 rows --claim proposed 14 rows, 5 of them copies of stored rows that earned nothing: MaxDepth=3 refusals, a form-bound record class, two nullable arrays ASP.NET names as one component. The axes no target pair names were set away from those, and the rows appended as 435–448.
then 12 json.MaxDepth=3 × shape.wrapped=own-record-class: all 26 stored rows holding the pair end as ASP.NET alone refusing the document, or inconclusive, at every position and host, so each obligation declares it inapplicable, citing record:0099 as it does Preserve. Rows 449–453 hold the other 11 pairs.
final 0

Probes replayed and reseeded

  • Replayed: replay --all from empty states, 45 of 45 passed each time: at 4558f41 and fe453f9 (with F4's first fix, never pushed), and at 158faa5, this branch merged with fix: a nullable value object that is a component keeps its null as a get-only property, a field and an oblivious one #63's head, which has the same tree as cfe942a (d536262). At cfe942a the gate ran against that last replay's ledger.
  • Reseeded: the harness changed, and it is in every probe's identity, so prober seeds --probe P ran for all 45 probes in two lanes. Each lane was a separate detached checkout at 158faa5 (tree d536262, the same as cfe942a) reading the unseeded rows from a copy of the final replay's ledger. Wall time 09:42 to 13:31.

170 seeds of 45 probes, 170 caught, each by a check of its own obligation; none missed, no seeded build failed.

Every seed, with the row and the check that caught it
Probe Seed's obligation Outcome Rows violating; e.g. the row and the check that caught it
BaseProbe none (claim) caught 55 of 411 rows; #10 claim
C01DecimalSpellingProbe covers-every-decimal caught 4 of 117 rows; #32 covers-every-decimal
C01DecimalSpellingProbe keeps-the-scale-it-reads caught 64 of 117 rows; #0 keeps-the-scale-it-reads
C01DecimalSpellingProbe number-is-a-number caught 43 of 117 rows; #0 number-is-a-number
C01DecimalSpellingProbe reads-a-number-or-a-string caught 101 of 117 rows; #0 reads-a-number-or-a-string
C01DecimalSpellingProbe smallest-scale caught 57 of 117 rows; #0 smallest-scale
C01DecimalSpellingProbe string-is-a-string caught 54 of 117 rows; #1 string-is-a-string
C02OptionalTextProbe hidden-is-an-error-in-both caught 9 of 50 rows; #3 hidden-is-an-error-in-both
C02OptionalTextProbe null-is-blank caught 7 of 50 rows; #6 null-is-blank
C02OptionalTextProbe optional-blank-is-no-value caught 15 of 50 rows; #0 optional-blank-is-no-value
C02OptionalTextProbe required-blank-is-an-error caught 11 of 50 rows; #2 required-blank-is-an-error
C03ComposedProbe detail-and-rule-intact caught 163 of 648 rows; #2 detail-and-rule-intact
C03ComposedProbe each-error-own-set caught 487 of 648 rows; #0 each-error-own-set
C03ComposedProbe shared-status-types caught 182 of 648 rows; #1 shared-status-types
C03ComposedProbe statuses-ascending caught 182 of 648 rows; #1 statuses-ascending
C03ComposedProbe unmapped-set-stops-start caught 648 of 648 rows; #0 unmapped-set-stops-start
C04WithoutFaultsProbe doc-omits-left-out caught 78 of 297 rows; #1 doc-omits-left-out
C04WithoutFaultsProbe left-out-everywhere-stops-start caught 59 of 297 rows; #11 left-out-everywhere-stops-start
C04WithoutFaultsProbe narrows-wider-status caught 93 of 297 rows; #0 narrows-wider-status
C04WithoutFaultsProbe reporting-left-out-throws caught 238 of 297 rows; #0 reporting-left-out-throws
C05RuleProbe nested-chain caught 125 of 645 rows; #3 nested-chain
C05RuleProbe no-namespace caught 247 of 645 rows; #2 no-namespace
C05RuleProbe rule-qualified caught 129 of 645 rows; #4 rule-qualified
C05RuleProbe stays-the-errors caught 376 of 645 rows; #2 stays-the-errors
C05RuleProbe unnamed-value-throws caught 269 of 645 rows; #0 unnamed-value-throws
C06FaultKindsProbe description-as-detail caught 260 of 306 rows; #1 description-as-detail
C06FaultKindsProbe kind-gives-status caught 131 of 306 rows; #3 kind-gives-status
C06FaultKindsProbe member-must-have-kind caught 306 of 306 rows; #0 member-must-have-kind
C06FaultKindsProbe no-description-no-detail caught 306 of 306 rows; #0 no-description-no-detail
C07BlanketMappingProbe by-kind caught 40 of 411 rows; #22 by-kind
C07BlanketMappingProbe enum-needs-own-mapping caught 66 of 411 rows; #0 enum-needs-own-mapping
C07BlanketMappingProbe mapping-refusal-stops-start caught 70 of 411 rows; #4 mapping-refusal-stops-start
C07BlanketMappingProbe member-without-kind-stops-start caught 33 of 411 rows; #7 member-without-kind-stops-start
C07BlanketMappingProbe own-mapping caught 27 of 411 rows; #2 own-mapping
C07BlanketMappingProbe own-set-keeps-mapping caught 64 of 411 rows; #1 own-set-keeps-mapping
C08SameNameWarningProbe bare-names-warn caught 90 of 302 rows; #0 bare-names-warn
C08SameNameWarningProbe leave-one-out-fixes caught 138 of 302 rows; #1 leave-one-out-fixes
C08SameNameWarningProbe qualified-chain-warns caught 74 of 302 rows; #8 qualified-chain-warns
C08SameNameWarningProbe qualified-names-fix caught 74 of 302 rows; #8 qualified-names-fix
C08SameNameWarningProbe warning-names-both caught 164 of 302 rows; #0 warning-names-both
C09BaseRecordEqualityProbe equal-by-the-wrapped-value caught 21 of 101 rows; #1 equal-by-the-wrapped-value
C09BaseRecordEqualityProbe in-a-collection-of-the-base caught 12 of 101 rows; #12 in-a-collection-of-the-base
C10PreserveGuardProbe ignore-cycles-unaffected caught 4 of 306 rows; #72 ignore-cycles-unaffected
C10PreserveGuardProbe own-converters-unaffected caught 15 of 306 rows; #1 own-converters-unaffected
C10PreserveGuardProbe refused-where-references-could-be-written caught 4 of 306 rows; #221 refused-where-references-could-be-written
C10PreserveGuardProbe value-converters-unaffected caught 4 of 306 rows; #59 value-converters-unaffected
C11ParseShapesBuildProbe abstract-string-constructor caught 22 of 247 rows; #3 abstract-string-constructor
C11ParseShapesBuildProbe implicit-result caught 28 of 247 rows; #2 implicit-result
C11ParseShapesBuildProbe parsing-where-it-can-be-parsed caught 16 of 247 rows; #37 parsing-where-it-can-be-parsed
C11ParseShapesBuildProbe ref-text caught 22 of 247 rows; #4 ref-text
C12DescribedConverterProbe every-position caught 296 of 462 rows; #1 every-position
C12DescribedConverterProbe options-describe caught 76 of 462 rows; #4 options-describe
C12DescribedConverterProbe own-converter-describes-itself caught 220 of 462 rows; #1 own-converter-describes-itself
C12DescribedConverterProbe parameter-keeps-aspnet caught 132 of 462 rows; #2 parameter-keeps-aspnet
C13PatchRequiredProbe absent-is-default caught 219 of 321 rows; #0 absent-is-default
C13PatchRequiredProbe doc-optional-where-absent caught 130 of 321 rows; #0 doc-optional-where-absent
C13PatchRequiredProbe doc-required-where-rejected caught 38 of 321 rows; #4 doc-required-where-rejected
C14DeclaredProblemProbe application-json-declared caught 38 of 421 rows; #1 application-json-declared
C14DeclaredProblemProbe ascending-with-declared caught 105 of 421 rows; #1 ascending-with-declared
C14DeclaredProblemProbe declared-describes-fault caught 30 of 421 rows; #15 declared-describes-fault
C14DeclaredProblemProbe declared-names-types caught 77 of 421 rows; #1 declared-names-types
C15MvcFaultStatusDocProbe builder-reaches-doc caught 24 of 405 rows; #30 builder-reaches-doc
C15MvcFaultStatusDocProbe status-per-scope caught 76 of 405 rows; #0 status-per-scope
C15MvcFaultStatusDocProbe without-faults-leaves-doc caught 45 of 405 rows; #20 without-faults-leaves-doc
C16OptionsAgreementProbe differing-refused caught 55 of 298 rows; #8 differing-refused
C16OptionsAgreementProbe error-names-type-and-descriptions caught 55 of 298 rows; #8 error-names-type-and-descriptions
C16OptionsAgreementProbe minimal-host-not-compared caught 48 of 298 rows; #0 minimal-host-not-compared
C16OptionsAgreementProbe patch-held-refused caught 55 of 298 rows; #8 patch-held-refused
C16OptionsAgreementProbe same-converters-build caught 122 of 298 rows; #1 same-converters-build
C17Cmtk0014Probe a-cast-naming-no-member caught 78 of 295 rows; #4 a-cast-naming-no-member
C17Cmtk0014Probe a-member-is-silent caught 46 of 295 rows; #3 a-member-is-silent
C17Cmtk0014Probe constructor-accessibility-too caught 38 of 295 rows; #4 constructor-accessibility-too
C17Cmtk0014Probe default-and-zero caught 129 of 295 rows; #0 default-and-zero
C17Cmtk0014Probe reported-where-written caught 142 of 295 rows; #0 reported-where-written
C18Cmtk1023Probe a-warning-and-generated caught 280 of 280 rows; #0 a-warning-and-generated
C18Cmtk1023Probe generated-equality-never-calls-the-base caught 83 of 280 rows; #4 generated-equality-never-calls-the-base
C18Cmtk1023Probe names-each-base-member caught 130 of 280 rows; #0 names-each-base-member
C19Cmtk0010KindsProbe a-pair-once-at-the-later-argument caught 84 of 297 rows; #6 a-pair-once-at-the-later-argument
C19Cmtk0010KindsProbe flags-enum-and-read-only-struct caught 34 of 297 rows; #2 flags-enum-and-read-only-struct
C19Cmtk0010KindsProbe interface-and-unsealed caught 35 of 297 rows; #1 interface-and-unsealed
C19Cmtk0010KindsProbe methods-the-compiler-calls caught 61 of 297 rows; #6 methods-the-compiler-calls
C19Cmtk0010KindsProbe value-object-and-closed-set caught 32 of 297 rows; #0 value-object-and-closed-set
C20ShouldHoldProbe a-failure-says-the-outcome caught 122 of 261 rows; #2 a-failure-says-the-outcome
C20ShouldHoldProbe fails-unless-it-holds-that-reason caught 111 of 261 rows; #0 fails-unless-it-holds-that-reason
C20ShouldHoldProbe hands-back-detail-and-rule caught 111 of 261 rows; #0 hands-back-detail-and-rule
C20ShouldHoldProbe uninitialized-fails caught 28 of 261 rows; #6 uninitialized-fails
C20ShouldHoldProbe whichever-of-its-types caught 120 of 261 rows; #1 whichever-of-its-types
C21OpenapiSchemaParityProbe container-elements caught 34 of 454 rows; #0 container-elements
C21OpenapiSchemaParityProbe dictionary-key caught 10 of 454 rows; #77 dictionary-key
C21OpenapiSchemaParityProbe every-document-version caught 105 of 454 rows; #4 every-document-version
C21OpenapiSchemaParityProbe nullable-admits-null-as-the-wrapped-does caught 15 of 454 rows; #11 nullable-admits-null-as-the-wrapped-does
C21OpenapiSchemaParityProbe the-underlying-types-schema caught 26 of 454 rows; #7 the-underlying-types-schema
C22GeneratorRefusalsProbe abstract caught 20 of 153 rows; #4 abstract
C22GeneratorRefusalsProbe closed-set-named-as-generated-member caught 4 of 153 rows; #11 closed-set-named-as-generated-member
C22GeneratorRefusalsProbe equality-operator caught 22 of 153 rows; #0 equality-operator
C22GeneratorRefusalsProbe every-error-links-the-table caught 85 of 153 rows; #1 every-error-links-the-table
C22GeneratorRefusalsProbe generic-wrapped caught 25 of 153 rows; #3 generic-wrapped
C22GeneratorRefusalsProbe named-as-generated-member caught 22 of 153 rows; #2 named-as-generated-member
C22GeneratorRefusalsProbe no-type-half-generated caught 85 of 153 rows; #1 no-type-half-generated
C22GeneratorRefusalsProbe over-object caught 6 of 153 rows; #15 over-object
C22GeneratorRefusalsProbe over-system-enum caught 7 of 153 rows; #8 over-system-enum
C22GeneratorRefusalsProbe over-value-object caught 23 of 153 rows; #1 over-value-object
C22GeneratorRefusalsProbe unsealed-record-class caught 22 of 153 rows; #5 unsealed-record-class
E01ColumnParityProbe column-type caught 12 of 175 rows; #0 column-type
E01ColumnParityProbe facet-kept caught 108 of 175 rows; #1 facet-kept
E01ColumnParityProbe nullable caught 36 of 175 rows; #1 nullable
E02ValueTranslatesProbe column-not-cast caught 206 of 218 rows; #0 column-not-cast
E02ValueTranslatesProbe get-value caught 59 of 218 rows; #1 get-value
E02ValueTranslatesProbe value-or-null caught 52 of 218 rows; #2 value-or-null
E02ValueTranslatesProbe value caught 59 of 218 rows; #0 value
E03ColumnRoundTripProbe keys caught 35 of 177 rows; #0 keys
E03ColumnRoundTripProbe nullable-properties caught 35 of 177 rows; #1 nullable-properties
E03ColumnRoundTripProbe stored-as-underlying caught 5 of 177 rows; #13 stored-as-underlying
E04IntegerKeyProbe byte-not-generated caught 1 of 28 rows; #9 byte-not-generated
E04IntegerKeyProbe char-key-yours caught 1 of 28 rows; #16 char-key-yours
E04IntegerKeyProbe generated-on-add caught 5 of 28 rows; #8 generated-on-add
E04IntegerKeyProbe guid-key-yours caught 1 of 28 rows; #12 guid-key-yours
E04IntegerKeyProbe not-on-efs-list caught 1 of 28 rows; #7 not-on-efs-list
E04IntegerKeyProbe own-configuration-wins caught 2 of 28 rows; #18 own-configuration-wins
E05OwnCompareToProbe comparison caught 30 of 90 rows; #0 comparison
E05OwnCompareToProbe equality-allowed caught 6 of 90 rows; #1 equality-allowed
E05OwnCompareToProbe min-max caught 12 of 90 rows; #4 min-max
E05OwnCompareToProbe ordering caught 24 of 90 rows; #7 ordering
E05OwnCompareToProbe query-filter caught 12 of 90 rows; #2 query-filter
E05OwnCompareToProbe value-order-allowed caught 6 of 90 rows; #11 value-order-allowed
E06OwnConverterNoValueProbe value-not-translated caught 5 of 12 rows; #1 value-not-translated
E06OwnConverterNoValueProbe value-object-comparison caught 7 of 12 rows; #0 value-object-comparison
E07ClosedSetNameProbe in-collections-and-json caught 2 of 9 rows; #2 in-collections-and-json
E07ClosedSetNameProbe non-member-throws caught 3 of 9 rows; #3 non-member-throws
E07ClosedSetNameProbe stored-as-name caught 3 of 9 rows; #2 stored-as-name
E07ClosedSetNameProbe unknown-name-throws caught 3 of 9 rows; #0 unknown-name-throws
E08OptionResultNotColumnsProbe error-says-so caught 20 of 20 rows; #0 error-says-so
E08OptionResultNotColumnsProbe model-fails caught 10 of 20 rows; #4 model-fails
E09OrNoneInDbProbe element-at-skips caught 3 of 18 rows; #4 element-at-skips
E09OrNoneInDbProbe first-reads-one-row caught 3 of 18 rows; #2 first-reads-one-row
E09OrNoneInDbProbe single-asks-for-two caught 1 of 18 rows; #9 single-asks-for-two
E10ReadBackSkipsValidationProbe reads-without-create caught 30 of 30 rows; #0 reads-without-create
E10ReadBackSkipsValidationProbe revalidate-finds caught 30 of 30 rows; #0 revalidate-finds
E11UseTypeKitOrderProbe order-does-not-matter caught 14 of 14 rows; #0 order-does-not-matter
E12ConventionByTypeProbe underlying-type-does-not-reach caught 7 of 12 rows; #0 underlying-type-does-not-reach
E12ConventionByTypeProbe value-object-type-reaches caught 4 of 12 rows; #1 value-object-type-reaches
E13PrimitiveCollectionProbe contains caught 5 of 10 rows; #1 contains
E13PrimitiveCollectionProbe element-value caught 5 of 10 rows; #0 element-value
E14InheritanceKeyProbe tpc-assign-on-sqlite caught 2 of 9 rows; #4 tpc-assign-on-sqlite
E14InheritanceKeyProbe tph-tpt-root caught 4 of 9 rows; #0 tph-tpt-root
E15CompositeKeyProbe as-for-primitives caught 6 of 8 rows; #0 as-for-primitives
E16OwnConfigurationWinsProbe column-type caught 5 of 25 rows; #0 column-type
E16OwnConfigurationWinsProbe conversion caught 5 of 25 rows; #1 conversion
E16OwnConfigurationWinsProbe never caught 2 of 25 rows; #17 never
E16OwnConfigurationWinsProbe on-add caught 2 of 25 rows; #9 on-add
E16OwnConfigurationWinsProbe optional caught 5 of 25 rows; #2 optional
L01JsonBytesProbe as-a-dictionary-key-too caught 13 of 585 rows; #56 as-a-dictionary-key-too
L01JsonBytesProbe read-as-the-underlying-type-reads caught 26 of 585 rows; #20 read-as-the-underlying-type-reads
L01JsonBytesProbe the-underlying-types-json caught 12 of 585 rows; #11 the-underlying-types-json
L02NoQuoteProbe json-never-repeats caught 135 of 366 rows; #0 json-never-repeats
L02NoQuoteProbe no-inner-that-quotes caught 8 of 366 rows; #5 no-inner-that-quotes
L02NoQuoteProbe parsing-never-repeats caught 103 of 366 rows; #1 parsing-never-repeats
L03OneRuleProbe json-runs-create caught 157 of 588 rows; #0 json-runs-create
L03OneRuleProbe parsing-runs-create caught 109 of 588 rows; #3 parsing-runs-create
L03OneRuleProbe refusals-name-the-fault caught 196 of 588 rows; #0 refusals-name-the-fault
L03OneRuleProbe tryfrom-runs-create caught 30 of 588 rows; #7 tryfrom-runs-create
L04RoundTripProbe invariant-whatever-the-culture caught 21 of 290 rows; #15 invariant-whatever-the-culture
L04RoundTripProbe json-refusal-is-a-json-exception caught 45 of 290 rows; #10 json-refusal-is-a-json-exception
L04RoundTripProbe text-reads-back caught 1 of 290 rows; #64 text-reads-back
L04RoundTripProbe the-type-converter-writes-the-same caught 5 of 290 rows; #14 the-type-converter-writes-the-same
L05EqualityProbe equality-is-the-underlying-values caught 22 of 309 rows; #2 equality-is-the-underlying-values
L05EqualityProbe ordering-follows-the-underlying-type caught 238 of 309 rows; #0 ordering-follows-the-underlying-type
L05EqualityProbe through-every-base caught 41 of 309 rows; #0 through-every-base
L06OffTheWireProbe never-read caught 268 of 326 rows; #0 never-read
L06OffTheWireProbe never-written caught 269 of 326 rows; #0 never-written

Tests

Through build-verdict test --project P at 60de568: CodoMetis.TypeKit.Prober.Tests 270 passed, CodoMetis.TypeKit.Conventions.Tests 1,501 passed, CodoMetis.TypeKit.Facts 91 passed and 9 skipped (the PostgreSQL facts, without TYPEKIT_PG; KB042 is new, and fails with its setting removed). F4's tests are in #63. docs-query check: 0 problems.

The gate's final output

probe gate: build/gate-closes passes since v2.1.0: 44 claim(s) touched and fully covered, every probe replayed and passing, no open finding.

🤖 Generated with Claude Code

CaffeinatedCoder and others added 11 commits October 18, 2026 02:44
…tches

C22GeneratorRefusalsProbe builds its consumer in a child process, so the
coverage report of its own process shows no file under src/, and
`prober footprint` refused to record an empty footprint ("it would never go
stale"), however many paths its registry entry watched. With no footprint
the probe was never current, and the gate counted all 621 of
C22-generator-refusals' pairs uncovered.

`footprint` now records an empty footprint where the entry watches
something, and refuses only where nothing ran and nothing is watched; a
footprint is current by its watched paths when it ran nothing under src/.
C22's entry watches what its consumer compiles against: the base package,
the analyzers it carries, the generators, and src/Directory.Build.* (it
watched the first and the last two, as C18 does, and lacked the generators
and the analyzers).

Recovers 607 of C22's 621 pairs, the rest of what its rows hold; its
other 14 pairs had no row and get rows with the scattered pairs.

Tests: WatchedTests gains a probe that ran nothing under src/ (recorded by
what it watches, current, and stale when a watched path changes) and one
that also watches nothing (refused). Each fails with its half of the change
reverted, and the refusal test fails with the refusal removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…for the rest of C12, C13, C16 and C21

Decision 108 stopped crediting checks that compared nothing but a document
both hosts refuse, which left 821 pairs of C12, 48 of C13, 455 of C16 and
677 of C21 uncovered.

Declarations (claims.json, `inapplicable`, cite record:0099):

- json.ReferenceHandler=Preserve on every obligation of C12, C16 and C21,
  and on C13's two document obligations (doc-required-where-rejected,
  doc-optional-where-absent); absent-is-default reads the wire and stays
  wanted there. Measured at 938d8f8 from an empty state: every row of the
  four probes under Preserve had its document refused by ASP.NET alone too
  (C12 76, C13 71, C16 93, C21 63 rows), none written.
- json.MaxDepth=3 together with source IDescribedJsonConverter or
  JsonSchemaDescriptions on C12's four obligations: the coord host's
  document is refused by ASP.NET alone in every such row (106 rows at
  938d8f8, 166 after the new rows, none written), while under the
  decimal-converter source it is written in every row (29 and 55).

No declaration under json.MaxDepth=3 alone: the documents of C12's decimal
host, of C13 (47 rows), C16 (91) and C21 (59) are written and compared
there, so it would not be true, and its pairs were covered already.

Rows: `rows --probe` for C12, C13, C16 and C21 (C13's came out the same,
since Preserve stays wanted by absent-is-default), then rows from
`rows --claim` appended until the pairs converged: C12 302 -> 462 rows, C13
296 -> 321, C16 298, C21 295 -> 435. Under MaxDepth=3 without Preserve about
two thirds of the documents are refused for ASP.NET alone too, decided by
the depth of the row's shape rather than by an axis, so those pairs needed
further rows rather than a declaration.

Recovers C12 821 (647 by rows, 174 declared), C13 48, C16 455 (283 by
rows, 172 declared), and 664 of C21's 698 (498 by rows, 166 declared).
C21's regenerated rows fail in 8 rows and its appended rows in 2 more;
their triage is in the next commits and in the pull request.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…bject admits null and ASP.NET drops it

Decision 36: "A nullable value object admits null in the OpenAPI document
wherever it appears". C21 quoted it as `a-class-value-object-admits-null`
for the class kinds only; a nullable struct value object admits null the
same way, so the entry `a-nullable-value-object-admits-null` quotes the same
sentence for http.nullability=nullable.

Triage step 3 of row 216 of C21OpenapiSchemaParityProbe (a validated struct
over double, nullable, in a read-only list, under
AllowNamedFloatingPointLiterals): ASP.NET writes the items of the twin's
IReadOnlyList<double?> without null, on the host without TypeKit as well,
and the value object's items are its component or null, as decision 36
says; the schemas are otherwise the same. No finding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…at, decision 36 for an added null, and cannot tell a form-bound record class

Triage of the rows C21OpenapiSchemaParityProbe failed after its rows were
written again; each failed in three runs at 938d8f8 (two replays and a try).

- Rows 113 and 282 (triage 2, KB-001): a class value object over TimeOnly
  and over char at the root, nullable, under OpenAPI 2.0. The minimal
  schema endpoint's twin body is TimeOnly? / char?, which ASP.NET writes as
  its format and allOf [{x-nullable: true}] alone, on the host without
  TypeKit as well; the class value object's body is not nullable in a
  minimal API and keeps its schema. KB-001 (executable, KB001.cs) says a
  nullable body is written as "its format and allOf: [{"x-nullable": true}]";
  the harness cited not-promised openapi-2-nullable-body-keeps-only-its-null
  only where the body kept nothing but its null (an enum), and now cites it
  where it keeps its format too.
- Row 216 (triage 3, decision 36): the value object admits null where
  ASP.NET writes the twin's nullable type without it, the schemas otherwise
  the same: cites a-nullable-value-object-admits-null (previous commit).
- Row 62 (triage 1): a validated class over a record class, bound from an
  MVC form. ASP.NET binds the wrapped record class by its properties (A, B)
  and the value object otherwise (Value.A, Value.B); the harness already
  ended parameters that differ this way inconclusive, and now does the
  same for a form body.

Rows 38, 170, 184, 196, 330, 358, 386, 408, 415 and 422 still fail; the
pull request carries their triage (code-analyst hand-offs and the harness
gaps left).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hey substituted

The stored rows of C17Cmtk0014Probe and C19Cmtk0010KindsProbe put
shape.refused-kind values in rows whose other settings exclude them; the
probes substituted another kind there and credited values the row did not
run. `rows --probe` now leaves such an axis out of the row (decision 108),
so both files are written again whole: C17 283 -> 295 rows (14 leave an
axis out), C19 282 -> 297 (14 leave an axis out). Both replay with every
row passing.

Recovers C17-cmtk0014's 41 pairs and C19-cmtk0010-kinds' 50.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, and its JSON check reads a value of another kind

L02 (README "Rejections never repeat the input") left 74 pairs uncovered,
6 of them json.Converters=own-for-wrapped|own-for-value-object against
text.entry=TryFrom|ToString-format|TryFormat-short-span: in such a row the
JSON check is declared inapplicable (not-promised
the-applications-own-converter) and nothing else ran.

What is true there, and so what this commit says: TryFrom, ToString-format
and TryFormat-short-span reject no text. TryFrom answers None, and
formatting reads no input; the Generators README's "When text is rejected"
names the rejections, a FormatException from Parse and false from
TryParse. The probe already runs its parsing checks only for Parse,
TryParse, IParsable, ISpanParsable and the type converter (TextRow.Reads),
which are the `axes` both obligations list. So parsing-never-repeats and
no-inner-that-quotes declare themselves inapplicable at those three entries
(cite docs:src/CodoMetis.TypeKit.Generators/README.md#when-text-is-rejected).
The 6 pairs are then wanted by no obligation; every other pair of those
entries stays wanted by json-never-repeats. Rows could not reach them: no
check of the claim applies there.

json-never-repeats placed the secret only where the wrapped value stands,
as a string, and an unvalidated value object over string or Uri reads every
string, so such rows ended "every JSON input read, none refused". Where
every variant was read, the check now also sends an array holding the
secret in that place, which such a value object refuses, and requires the
refusal not to quote it (not at a dictionary key, which is a property
name). 22 of the 26 rows inconclusive that way now pass, none fails.

Exclusions: json.PreferredObjectCreationHandling=Populate against a
reference handler, which System.Text.Json refuses (JsonRow substitutes the
default), as twelve other claims exclude it; and a record class as a
dictionary key, which System.Text.Json writes only through a converter of
the application's (NotSupportedException for the wrapped type alike), where
the JSON half is not promised, and which has no parsing.

Rows: `rows --probe` (289 rows), then rows from `rows --claim` appended,
and 8 rows set by hand from a passing row's settings for pairs the covering
array kept placing in rows that read nothing: own-for-value-object with
Parse, a record class with a wrong token and as a nullable property, Uri
and TryFrom as a field (with IncludeFields), and an enum with ISpanParsable;
366 rows.

Recovers all 74: 6 declared, 3 excluded, 65 by rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…g, bool and Uri, and holds group-by at two levels over DateTimeOffset

E02ValueTranslatesProbe built where-value-compare with
Expression.GreaterThanOrEqual and, where that throws, substituted
where-equals ("the wrapped type has no >= operator"), so 5 pairs of
ef.query=where-value-compare never ran: string, bool, Uri, enum and flags
enum. The reason is true of the first three, which have no >= in C#, and
not of an enum, which C# compares with >= over its underlying type: an
expression tree the compiler writes for `o.Status.Value >= Status.B`
converts both sides to it.

- The probe now compares an enum (or a nullable enum) as C# does, through
  the underlying type on both sides; rows 216 and 217 (an enum and a flags
  enum) run where-value-compare and pass, the rows and the SQL the twin's.
- claims.json excludes where-value-compare over string, bool and Uri with
  that reason.
- One group-by row for shape.site=nested-two-levels x
  shape.wrapped=DateTimeOffset (row 215), and the two rows above, appended
  from `rows --claim`.

Recovers all 6 of E02's pairs: 3 excluded, 3 by rows.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…01, L03, L04, L06 and C22

Exclusions, each with its reason:

- json.PreferredObjectCreationHandling=Populate against
  json.ReferenceHandler=Preserve|IgnoreCycles for C03, C05, L01, L03 and
  L04, the reason twelve other claims already give: System.Text.Json refuses
  Populate with a reference handler, and JsonRow substitutes the default.
  Recovers C03's 2, C05's 2, L04's 2, and 2 each of L01 and L03.
- C02: AsText and AsOptionalText in globalization-invariant mode, not
  promised (as-text-refused-in-globalization-invariant-mode): AsText()
  throws PlatformNotSupportedException there (docs/text.md, "Globalization-
  invariant mode") and AsOptionalText() calls it; the probe checks that
  refusal without credit and runs the row on the identifier counterpart.
  2 pairs.

Rows, appended from `rows --claim` and replayed:

- C02: 4 rows in globalization-invariant mode (the other 5 of its 7 pairs).
- L01: 3 rows (read-only-list-element x flags enum, wrong-token x enum,
  and a record class as a dictionary key, row 582, which ends
  framework-refuses), and row 584, set by hand from row 582: a record class
  as a dictionary key under json.Converters=own-for-wrapped, the only
  converter under which System.Text.Json writes a record class as a key
  (row 582 and every earlier row with the pair end framework-refuses,
  NotSupportedException for the twin alike). Recovers L01's other 3.
- L03: 2 rows (polymorphic-derived-property x a record class, TimeOnly x
  ToString-format, a record class x Parse). Recovers L03's other 3.
- L06: 1 row, field x string under IncludeFields; ProbeContext gains its
  carrier (Shapes.g.cs, written again by shapes.cs from L06's rows, as the
  generators' README says). Recovers L06's 1.
- C22: 9 rows for the 14 pairs no row held once its footprint was current
  (shape.inheritance, shape.kind and shape.site against DateTime,
  DateTimeOffset, DateOnly, Uri, long and char).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d C21's obligations do not apply there

Triage step 2 of row 408 of C21OpenapiSchemaParityProbe (a validated class
over char as a polymorphic derived property, json.MaxDepth=3 and
json.NumberHandling=WriteAsString); it failed in three runs of `try` at
53050b7. AddTypeKit() refused the document with a JsonException reading an
OpenApiJsonSchema, ASP.NET alone with MaxDepth's InvalidOperationException:
two refusals that differ by type, which decision 108 makes a violation
unless a sentence makes them intended. Neither is TypeKit's.

ASP.NET builds each schema with System.Text.Json's exporter under the host's
options and reads the node back as its own OpenApiJsonSchema; a char's
schema is minLength and maxLength 1, which reach the reader as strings under
WriteAsString. Measured in a host without TypeKit (KB042.cs, its fixture):
a body holding a char is refused so, one holding an int is written, and so
is a char under the default number handling. With MaxDepth left at its
default, ASP.NET alone refuses row 408's document with this same
JsonException; under MaxDepth=3 it meets the depth limit first at the
twin's endpoint, where TypeKit asks for the char while it describes the
value object's.

So no document is written for a char under WriteAsString, by either host:
each obligation of C21 declares the pair json.NumberHandling=WriteAsString x
shape.wrapped=char inapplicable, citing fact:KB-042. No finding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…first-use null, a 2.0 nullable element, or a library's converter

Triage step 3 of rows 196, 358 and 422 of C21OpenapiSchemaParityProbe, each
failing in three runs of `try` at 53050b7; the harness cites the entries in
the next commit.

- Row 196 (a validated struct over an enum written by name, a polymorphic
  derived type's property): the twin's property refers to the enum's
  component, which lists null because ASP.NET met it first as a nullable
  use, on the host without TypeKit as well; the value object's property is
  not nullable there and its component lists no null. KB-027 (executable)
  says so for every use: "met first as a nullable parameter, the component
  lists null among its values, for its non-nullable uses too". The entry
  enum-component-is-its-first-use quotes the sentence about parameters
  only; enum-component-lists-the-null-of-its-first-use quotes this one.
- Row 358 (a nullable validated struct over bool as a dictionary's value,
  OpenAPI 2.0): the value object's value is its component or null, which
  the 2.0 writer writes as allOf [{x-nullable: true}] without the
  reference (KB-026), where the twin's bool? keeps its type. The AspNetCore
  README said so of a property and named the element the component or null
  "as a nullable property is", but not that it loses its type under 2.0;
  it says so now, and openapi-2-nullable-element-loses-its-type quotes it.
- Row 422 (a nullable record struct over NodaTime's Instant in a list,
  OpenAPI 3.0): ASP.NET drops the twin's items, since it cannot see through
  NodaTime's converter, on the host without TypeKit as well, and the value
  object's items are put back. The README said so of "a JSON converter you
  write yourself", which own-converter-for-the-wrapped-type quotes under
  json.Converters=own-for-wrapped only; it now says the same of a converter
  a library registers for its own type, and
  a-librarys-converter-for-the-wrapped-type quotes it for
  shape.wrapped=NodaTime.Instant.

No finding.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…on and for an element, and KB-027 and a library's converter for a property

Triage of rows 196, 358, 386 and 422 of C21OpenapiSchemaParityProbe, each
failing in three runs of `try` at 53050b7; with this commit each ends
inapplicable with its citation in `try`.

- Row 386 (triage 3, KB-026): a nullable validated struct over Uri as a
  required-init property, OpenAPI 2.0. The value object's property is the
  2.0 null flag with the wrapped type's format beside it, the twin's Uri
  keeps its type. The harness cited openapi-2-nullable-component-loses-
  its-type only where side (1) wrote the same at the value object's path,
  before any normalisation; a nullable struct value object over a class
  had its null flag taken out by the spellings' normalisation first, which
  left only the missing type. It is now told by its structure: the format
  beside the null flag alone, and the twin's the same with its type.
- Row 358 (triage 3): a dictionary's nullable value is the null flag alone
  under 2.0, where ASP.NET alone drops a converter-backed element, so side
  (1) has nothing there; told by its structure, it cites
  openapi-2-nullable-element-loses-its-type.
- Row 196 (triage 3, KB-027): the branch that cites
  enum-component-is-its-first-use for a parameter now takes a property too,
  citing enum-component-lists-the-null-of-its-first-use, on the same
  evidence (the twin's enum is a resolved component, side (1) lists the
  same null).
- Row 422 (triage 3): a list of Instant whose twin has no items, side (1)
  neither, cites a-librarys-converter-for-the-wrapped-type.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CaffeinatedCoder and others added 5 commits October 18, 2026 09:23
…re MVC's formatters decide them, and tells KB-026 by the null flag beside a format

With F4's fix applied (its own pull request, fix/component-keeps-null),
`try` of all of C21OpenapiSchemaParityProbe failed rows 38, 178, 181 and 243;
each now passes or cites, and so does the whole probe.

- Row 38 (triage 1): with the null in place, the next difference was the
  MVC body's media types: the value object's lists application/xml,
  text/xml and application/*+xml, the twin's does not. Under
  mvc.InputFormatters=xml-added, XmlSerializerInputFormatter reads the
  sealed record class value object and not the Uri it wraps; ASP.NET alone,
  with both sides' endpoints, lists the same for each side. The harness
  compared which media types MVC's formatters accept for two .NET types,
  which the claim does not speak to. Where side (1) lists each side's media
  types as the document does, the schemas are now compared under the media
  types both list; otherwise as before, so a media type lost to TypeKit
  still fails.
- Rows 178, 181 and 243 (triage 3, KB-026, openapi-2-nullable-component-
  loses-its-type): class value objects over long, bool and int whose
  property admits null as a type parameter's or set through the
  constructor, OpenAPI 2.0. With F4's null around the reference, the 2.0
  writer writes the property as allOf [{x-nullable: true}] beside the
  wrapped type's format, without the type and pattern the twin keeps, as
  the AspNetCore README says. The branch row 386 needed is told the same
  way for any content the writer drops: the value object's property is the
  null flag, alone or beside a format, and the twin's admits null with the
  same format.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…twin is a component itself

`prober status` after a replay of the http family, with F4's fix applied: C21 had
13,677 of its 13,716 wanted pairs current, 39 uncovered. `rows --claim
C21-openapi-schema-parity` proposed 14 rows, 5 of them the same as stored
rows that earned nothing: under json.MaxDepth=3 both hosts refuse the
document (409, 426, 430), a record class bound from an MVC form is
inconclusive (411), and two nullable arrays ASP.NET names as one component
(425). In each proposed row the axes no target pair names were set apart
from those: json.MaxDepth and openapi.CreateSchemaReferenceId to their
defaults, an own-record-class's form binding to the body, and the array's
nullability to as-declared. The 14 are appended as rows 435 to 448.

Row 444 (triage 3, KB-026): a validated class over a record class as a
property, OpenAPI 2.0. The value object's property is the null flag alone;
the twin's is the record class's component, whose reference ASP.NET's own
rule leaves without a null, where the value object admits one (decision 36).
The structural KB-026 branch now takes a twin that is a resolved component
beside one that admits null.

`try` of all 449 rows: pass 292, expected-refusal 112, inapplicable 26,
inconclusive 19, no violation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…MaxDepth=3, and rows for its last 12 pairs

`prober gate --since v2.1.0` after `replay --all` with F4's fix applied: 45 probes
passed, C21 had 12 of its 13,716 wanted pairs uncovered, from rows 436, 440,
443 and 446 of the previous commit, which earned nothing.

- json.MaxDepth=3 x shape.wrapped=own-record-class: every one of the 26
  stored rows that holds the pair ends as ASP.NET alone refusing the
  document (the schema exporter's MaxDepth InvalidOperationException, at
  every position and host) or inconclusive; none can compare a document.
  Each obligation declares the pair inapplicable, citing record 0099, as
  it does json.ReferenceHandler=Preserve.
- The other 11 pairs get five rows (449 to 453), each from `rows --claim`
  with the axes its target pairs do not name set where the document
  compares like with like: json.MaxDepth at its default; an own record
  class or an Instant as declared, not nullable, since F4 has a nullable
  value object admit the null its twin's component lacks (decision 36);
  a struct value object over a record class made a validated class with
  everything inlined, since a struct over a class has no twin at its
  nullability; a list of Instant on the host with controllers alone, whose
  minimal schema endpoint drops the twin's items (not-promised
  a-librarys-converter-for-the-wrapped-type); bodies rather than a query
  for a record class, which ASP.NET binds by its properties.

`try` of rows 449 to 453: 5 pass.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
`prober gate --since v2.1.0` passes on probes alone, with no waiver: after
`replay --all` from an empty PROBE_STATE_DIR with F4's fix applied (its own
pull request), "44 claim(s) touched and fully covered, every probe replayed
and passing, no open finding". The item "Grow the probe corpus until
`prober gate` passes" goes, and so does the WS2 item's list of what the
harness left the gate (C12, C13, C16 and C21's document-only pairs, C22's
footprint), which this pull request answered. The roadmap holds what is
open.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
F4's fix (#63), which C21's rows 38, 170, 184, 330 and 415 need.

# Conflicts:
#	tools/probe/corpus/harness/HttpBaseline.cs
@CaffeinatedCoder CaffeinatedCoder changed the title build: answer the gate's refusals with rows, declarations and exclusions; C21 still refuses build: answer the gate's refusals with rows, declarations and exclusions; the gate passes Oct 18, 2026
@CaffeinatedCoder
CaffeinatedCoder merged commit fe9227a into release/2.2.0 Oct 18, 2026
13 checks passed
@CaffeinatedCoder
CaffeinatedCoder deleted the build/gate-closes branch October 18, 2026 11:47
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